2017-02-15 30 views
2

я был дан заряд реорганизовать старый код и наткнулся на этот случай:C++ выполнить действие для каждого типа в данном списке

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); 
    } 
} 

Это, очевидно, плохо, так как я копирую в основном тот же самый код в 3 раза поэтому я решил реорганизовать его, используя шаблоны, как это:

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>(); 
} 

есть ли другие более легкие/более продуктивные способы факторизовать такого рода повторения в коде?

Вопрос о бонусе: если мои типы не были статическими, но заданы в вариационном шаблоне, как бы я сделал подобное лечение?

+3

Я не вижу никаких проблем с вашей функцией 'doStuffBetter'. Выглядит легко и доступно для меня. –

+1

Учитывая, что у вас есть рабочий код, это может быть лучше подходит для [Code Review] (http: //codereview.stackexchange.com /) – lcs

+0

@lcs может быть, но вторая часть вопроса не имеет никакого рабочего кода – Nyashes

ответ

1

Я нахожу ваше решение достаточно хорошо.

Просто для удовольствия, я предлагаю следующее doStuffBetter()

template <typename ... Types> 
void myClass::doStuffBetter() 
{ 
    int unused[] { (do123<Types>(), 0)... }; 

    (void)unused; // to avoid a warning 
} 

Должен работать с C++ 11, и я полагаю, что может ответить на ваш бонусный вопрос тоже.

+0

Он определенно отвечает на вопрос о бонусе. Мне нравится, как вы использовали initializer_list для выполнения трюка. – Nyashes

+0

@AlexandreTHOUVENIN. Я не эксперт, и я не очень хорошо объясняю, но ... идея инициализируется массивом 'int', поэтому мы создаем среду, в которой мы может поместить последовательность значений, разделенных запятыми, и каждому значению для массива (нуль) предшествует значение ('do123 ()'), которое отбрасывается следующей запятой – max66

1

Вы можете использовать boost::mpl::vector<> как список типов и boost::mpl::for_each перебрать типов:

#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'; 
    }); 
} 

Выходы:

5Type1 
5Type2 
5Type3 

Если C++ 14 лямбда с auto недоступен , вместо лямбда-функции используйте следующий функциональный объект:

struct TypeCallback 
{ 
    template<class TypeWrapper> 
    void operator()(TypeWrapper) { 
     using Type = typename TypeWrapper::agrument_type; 
     // Place your code here. 
     std::cout << typeid(Type).name() << '\n'; 
    } 
}; 

И потом:

boost::mpl::for_each<Types, Type<boost::mpl::_1> >(TypeCallback{}); 

без наддува, вместо того, чтобы использовать boost::mpl::vector<> и boost::mpl::for_each, просто создать функцию, которая перебирает жестко закодированы список типов:

#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{}); 
} 
+0

Boost делает удивительные вещи, я не использую это в производственном коде, но определенно +1 для оригинальности. – Nyashes

+0

@AlexandreTHOUVENIN Добавлена ​​версия без Boost для вас. –

+0

Он приносит что-то интересное, менее элегантное, чем вектор типа, но он все еще сохраняет список типов где-то еще. Это может быть полезно. – Nyashes

1

Это решение C++ 14.

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)...}; 
    } 
} 

что-то готовое.

Сейчас мы делаем:

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); 
     } 
    } 
); 
} 

мы можем пойти еще дальше и обновить myIterator<_type> таким образом, чтобы экземпляр по умолчанию возведенных сравнивает равно один-мимо-окончания итератора.

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)}; 
    } 
} 

тогда мы получим:

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); 
     } 
    } 
); 
} 
+0

Мне очень нравится ваш for_each_type, даже если это C++ 14, я мог бы использовать такие решения в других проектах или даже позже в том же проекте +1, очевидно. Обновление итератора также хорошо, если использование его кода не было настолько огромным, я бы, вероятно, пошел на это – Nyashes