问题
I have been given the charge to refactor old code and have stumbled upon this case:
void myClass::doStuff()
{
for( myIterator< Type1 > it( this->getDatabase() ); it; ++it )
{
do1( *it );
do2( *it );
do3( *it );
}
for( myIterator< Type2 > it( this->getDatabase() ); it; ++it )
{
do1( *it );
do2( *it );
do3( *it );
}
for( myIterator< Type3 > it( this->getDatabase() ); it; ++it )
{
do1( *it );
do2( *it );
do3( *it );
}
}
It's obviously bad since I copy basically the same code 3 times so I decided to refactor it using templates like this :
template<class _type> void myClass::do123()
{
for( myIterator< _type > it( this->getDatabase() ); it; ++it )
{
do1( *it );
do2( *it );
do3( *it );
}
}
void myClass::doStuffBetter()
{
do123<Type1>();
do123<Type2>();
do123<Type3>();
}
Is there other easier/more productive ways to factorize this kind of repetition in code ?
Bonus question : if my types were not static but given in a variadic template how would I do a similar treatment ?
回答1:
I find your solution good enough.
Just for fun, I propose the following doStuffBetter()
template <typename ... Types>
void myClass::doStuffBetter()
{
int unused[] { (do123<Types>(), 0)... };
(void)unused; // to avoid a warning
}
Should work with C++11 and I suppose that can respond to your bonus question too.
回答2:
You can use boost::mpl::vector<>
as a list of types and boost::mpl::for_each
to iterate over the types:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <iostream>
#include <typeinfo>
struct Type1 {};
struct Type2 {};
struct Type3 {};
template<class T>
struct Type
{
typedef T agrument_type;
};
int main(int ac, char**) {
using Types = boost::mpl::vector<Type1, Type2, Type3>;
boost::mpl::for_each<Types, Type<boost::mpl::_1> >([](auto type_wrapper) {
using Type = typename decltype(type_wrapper)::agrument_type;
// Place your code here.
std::cout << typeid(Type).name() << '\n';
});
}
Outputs:
5Type1
5Type2
5Type3
If C++14 lambda with auto
is unavailable, use the following functional object instead of the lambda function:
struct TypeCallback
{
template<class TypeWrapper>
void operator()(TypeWrapper) {
using Type = typename TypeWrapper::agrument_type;
// Place your code here.
std::cout << typeid(Type).name() << '\n';
}
};
And then:
boost::mpl::for_each<Types, Type<boost::mpl::_1> >(TypeCallback{});
Without boost, instead of using boost::mpl::vector<>
and boost::mpl::for_each
, just create a function that iterates over a hard-coded list of types:
#include <iostream>
#include <typeinfo>
template<class T>
struct Type
{
typedef T agrument_type;
};
struct TypeCallback
{
template<class TypeWrapper>
void operator()(TypeWrapper) {
using Type = typename TypeWrapper::agrument_type;
// Place your code here.
std::cout << typeid(Type).name() << '\n';
}
};
struct Type1 {};
struct Type2 {};
struct Type3 {};
template<class F>
void for_each_mytype(F f) { // <--- one function per different list of types
f(Type<Type1>{});
f(Type<Type2>{});
f(Type<Type3>{});
}
int main(int ac, char**) {
for_each_mytype(TypeCallback{});
}
回答3:
This is a C++14 solution.
namespace notstd {
template<class T> struct tag_t { constexpr tag_t() {}; using type=T; };
template<class T> constexpr tag_t<T> tag{};
template<class Tag> using type_t = typename Tag::type;
template<class...Ts, class F>
void for_each_type(F&& f) {
using discard=int[];
(void)discard{ 0,(void(
f( tag<Ts> )
),0)...};
}
}
that is some boilerplate.
Now we do:
void myClass::doStuffBetter()
{
notstd::for_each_type<Type1,Type2,Type2>(
[&](auto tag){
using type=notstd::type_t<decltype(tag)>;
for( myIterator<type> it( getDatabase() ); it; ++it )
{
do1( *it );
do2( *it );
do3( *it );
}
}
);
}
we can go a step further and upgrade myIterator<_type>
such that a default-constructed instance compares equal to the one-past-the-end iterator.
namespace notstd {
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It s, It f = {} ) {
return {std::move(s), std::move(f)};
}
}
then we get:
void myClass::doStuffBetter()
{
notstd::for_each_type<Type1,Type2,Type2>(
[&](auto tag){
using type=notstd::type_t<decltype(tag)>;
for( auto&& e : notstd::range( myIterator<type>( getDatabase() ) )
{
do1( e );
do2( e );
do3( e );
}
}
);
}
来源:https://stackoverflow.com/questions/42255534/c-execute-an-action-for-each-type-in-a-given-list