2015-06-11 5 views
0

Предположим, что у меня есть несколько типов, связанных с вариантом. С другой стороны, у меня есть перечисление, на котором некоторые из предыдущих типов могут быть выведены из, так что я могу иметь время выполнения псевдо завод:Тип конструкции над перечислением и переадресация разного количества аргументов

#include <boost/variant.hpp> 

enum class Type { W, X, Y, Z }; 

struct A {}; 

struct B 
{ 
    B(int) {} 
}; 

struct C 
{ 
    C(int, int) {} 
}; 

using variant_t = boost::variant<A, B, C>; 

template<typename... Args> 
variant_t MakeVariantOverEnum(Type type, Args&&... args) 
{ 
    switch (type) 
    { 
     case Type::X: return B(std::forward<Args>(args)...); break; 
     case Type::Z: return C(std::forward<Args>(args)...); break;    
     default:  return A(std::forward<Args>(args)...); break; 
    } 
} 

// meant to be fully runtime 
const Type GetTypeFromIO() { return Type::Z; } 
const int GetFirstArgFromIO() { return 0; } 
const int GetSecondArgFromIO() { return 0; } 

int main() 
{ 
    const Type type = GetTypeFromIO(); 
    const int firstArg = GetFirstArgFromIO(); 
    const int secondArg = GetSecondArgFromIO();  

    variant_t newVariant; 

    if (firstArg != 0 && secondArg != 0) newVariant = MakeVariantOverEnum(type, firstArg, secondArg); 
    else if (firstArg != 0)    newVariant = MakeVariantOverEnum(type, firstArg); 
    else         newVariant = MakeVariantOverEnum(type); 
} 

2 вещи беспокоить меня в этом коде:

* * Как я могу получить только 1 вызов MakeVariantOverEnum, передав все аргументы и затем отбросив эти «недопустимые» случаи (== 0 в моем примере)? Могу ли я сделать это внутри MakeVariantOverEnum с помощью некоторого механизма SFINAE?

** Это не компилируется, поскольку компилятор пытается сопоставить все конструкторы со всеми аргументами:

main.cpp: In instantiation of 'variant_t MakeVariantOverEnum(Type, Args&& ...) [with Args = {const int&, const int&}; variant_t = boost::variant<A, B, C>]': 
main.cpp:44:100: required from here 
main.cpp:24:59: error: no matching function for call to 'B::B(const int&, const int&)' 
     case Type::X: return B(std::forward<Args>(args)...); break; 
                 ^
main.cpp:24:59: note: candidates are: 
main.cpp:9:2: note: B::B(int) 
    B(int) {} 
^
main.cpp:9:2: note: candidate expects 1 argument, 2 provided 
main.cpp:7:8: note: constexpr B::B(const B&) 
struct B 
     ^
main.cpp:7:8: note: candidate expects 1 argument, 2 provided 
main.cpp:7:8: note: constexpr B::B(B&&) 
main.cpp:7:8: note: candidate expects 1 argument, 2 provided 

И так далее для других типов ...

Так что мой вопрос : как я могу заставить его работать на этом этапе?

Спасибо!

PS: код будет здесь =>http://coliru.stacked-crooked.com/a/4bc1e326be27b3dd

ответ

0

Самый простой подход к вашей проблеме:

enum class Type { W, X, Y, Z }; 

struct A {}; 

struct B 
{ 
    B(int) {} 
}; 

struct C 
{ 
    C(int, int) {} 
}; 

using variant_t = boost::variant<A, B, C>; 

variant_t MakeVariantOverEnum(Type type, int param1, int param2) 
{ 
    switch (type) 
    { 
     case Type::X: return B(param1); 
     case Type::Z: return C(param1, param2);    
     default:  return A(); 
    } 
} 

// meant to be fully runtime 
const Type GetTypeFromIO() { return Type::Z; } 
const int GetFirstArgFromIO() { return 0; } 
const int GetSecondArgFromIO() { return 0; } 

int main() 
{ 
    const Type type = GetTypeFromIO(); 
    const int firstArg = GetFirstArgFromIO(); 
    const int secondArg = GetSecondArgFromIO();  

    variant_t newVariant = MakeVariantOverEnum(type, firstArg, secondArg); 
} 

VARIADIC шаблон просто делает вещи более сложными и не поможет вам в любом случае.

Если все ваши структуры используют одни и те же параметры (по крайней мере: если параметр используется, тот же тип используется везде), это сработает. Однако, если у вас есть разные типы параметров для разных объектов, вам нужно подумать, не будет ли проще просто переключаться в самой основной функции (а затем получить правильные параметры):

Предположим следующее расширение:

struct D 
{ 
    D(float) {} 
} 

Так что теперь вы вдруг ситуацию, когда ваш const int firstArg будет поплавок ... (что не будет работать для B и C однако)

в качестве альтернативы вы указать третий параметр float param3 и только использовать этот параметр при создании типа. (но тогда как насчет param1 и param2?)

Примечание: До сих пор можно было бы использовать какой-либо заводской шаблон, но это было бы немного сложнее (каждому объекту нужна собственная фабрика, которая получит правильные параметры и затем создайте правильную структуру ...)