C++ execute an action for each type in a given list

拈花ヽ惹草 提交于 2019-12-11 04:06:15

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!