2017-01-23 6 views
2

Я действительно надеялся, что смогу достичь этого, используя шаблоны C++ (11), но я столкнулся с некоторыми проблемами здесь. Таким образом, у нас есть пользовательский объект-указатель, который внутри может быть любого из этих типов: список объектов собственного типа/int/a char/bool/long/double/char * или любой другой примитивный тип, и это определяется флаг, который хранится в этом объекте. Существуют глобальные методы получения значений конкретных типов из этого объекта.C++ условные шаблоны Компиляция на основе типа данных

Теперь моя цель проста. Я знаю, что для моего случая, мой объект находится список таких объектов, поэтому я хотел бы написать такую ​​функцию, как это общий сценарий:

template <typename T> 
std::vector<T> List_To_Vector(Object& list) 
{ 
    std::vector<T> vec; 
    int listSize = Func_Size(list);  
    for (int i = 0; i < listSize; ++i) 
    { 
     //let's say this function gives list items one by one 
     Object arg = Func_Next(list); 
     if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value) 
      vec.push_back((T)Func_IntValue(arg)); 
     else if (std::is_same<T, float>::value || std::is_same<T, double>::value) 
      vec.push_back((T)Func_DoubleValue(arg)); 
     else if (std::is_same<T, std::string>::value || std::is_same<T, char*>::value) 
      vec.push_back((T)Func_StringValue(arg)); //Func_StringValue returns char*, so conversion to std::string should work 
     else if (std::is_same<T, bool>::value) 
      vec.push_back(Func_BoolValue(arg)); 
     else if (std::is_same<T, char>::value) 
      vec.push_back(Func_CharValue(arg)); 

     vec.push_back(val); 
    } 

    return vec; 
} 

int main() 
{ 
    Object listContainingStrings = GetListOfNames(); 
    Object listContainingNumbers = GetListOfNumbers(); 
    std::vector<string> vec1 = List_To_STD_Vector<string>(listContainingStrings); 
    std::vector<int> vec2 = List_To_STD_Vector<int>(listContainingNumbers); 
    return 0; 
} 

Проблема заключается в том, C++ жалуется здесь, как он пытается скомпилировать код, принимающий T = std :: string, и int для string или float для преобразования строк завершится с ошибкой. То, что я действительно хотел здесь, это способ скомпилировать внутреннюю часть кода, когда тип определяется как int, а не какой-либо другой тип. Я могу использовать специализацию или перегрузку функции шаблона, но тогда я думаю, что она действительно побеждает цель шаблонов здесь, и я мог бы просто написать 8 разных функций для 8 разных типов (например, List_To_String_Vector, List_To_Int_Vector и т. Д.).

Я также пробовал еще один взломать, используя reinterpret_cast < T * > по адресу каждого типа возврата, а затем разыменовывая его для добавления в вектор. Это сработало, но есть предупреждения компилятора, и я думаю, что это неопределенное поведение.

Есть ли способ сделать это правильно?

Спасибо!

+0

Это выглядит очень плохой дизайн. И если вы хотите, чтобы объект обрабатывал несколько типов, то почему бы не использовать ['std :: any'] (http://en.cppreference.com/w/cpp/utility/any) из предстоящего стандарта C++ 17 ? Или если у вас его еще нет, то [Boost any] (http: //www.boost.орг/док/ЛИЭС/1_63_0/DOC/HTML/any.html)? –

+0

Почему ваша отдельная функция 'Func_ * Value' вместо одного шаблона или набора перегрузки? – Quentin

+0

@Quentin: Из-за устаревшего кода, а также для выполнения языка программирования Схемы для совместимости с C++. –

ответ

4

The fundamental theorem of software engineering:

Мы можем решить любую проблему, введя дополнительный уровень косвенности.

List_To_Vector делает слишком много – как преобразование из Object в T, и заполнение vector; абстрагирует первое, и решение становится естественным. Во-первых, List_To_Vector:

template<typename T> 
std::vector<T> List_To_Vector(Object& list) { 
    std::vector<T> vec; 
    for (int i = 0, listSize = Func_Size(list); i < listSize; ++i) { 
     vec.push_back(Object_To<T>(Func_Next(list))); 
    } 
    return vec; 
} 

Теперь вы можете перегрузить или специализироваться Object_To по мере необходимости. Вот один из способов, с помощью SFINAE:

// not strictly necessary, but reduces noise a bit 
template<bool B, typename T = void> 
using enable_if_t = typename std::enable_if<B, T>::type; 

template<typename T> 
auto Object_To(Object arg) 
-> enable_if_t<std::is_same<T, int>{} || std::is_same<T, unsigned>{}, T> 
{ 
    return (T)Func_IntValue(arg); 
} 

template<typename T> 
auto Object_To(Object arg) 
-> enable_if_t<std::is_same<T, float>{} || std::is_same<T, double>{}, T> 
{ 
    return (T)Func_DoubleValue(arg); 
} 

template<typename T> 
auto Object_To(Object arg) 
-> enable_if_t<std::is_same<T, std::string>{} || std::is_same<T, char*>{}, T> 
{ 
    return (T)Func_StringValue(arg); 
} 

template<typename T> 
auto Object_To(Object arg) -> enable_if_t<std::is_same<T, bool>{}, T> 
{ 
    return Func_BoolValue(arg); 
} 

template<typename T> 
auto Object_To(Object arg) -> enable_if_t<std::is_same<T, char>{}, T> 
{ 
    return Func_CharValue(arg); 
} 

Используя что-то вроде boost::fusion::map<> может сделать это гораздо чище, если вы можете позволить себе зависимость.

+0

@PiyushSoni: Если вы публикуете [MVCE] (https://stackoverflow.com/help/mcve), я могу добавить онлайн-демонстрацию к ответу. – ildjarn

2

Давайте действовать на один уровень ниже вашей функции List_To_Vector.Проблема, которую я вижу, что у вас есть набор несвязанных Func_*Value функций, так что давайте собирать их под единый тип-Aware шаблон один раз и навсегда:

template <class> 
struct valueGetter; 

template <> struct valueGetter<float> { static constexpr auto &get = Func_DoubleValue; }; 
template <> struct valueGetter<double> { static constexpr auto &get = Func_DoubleValue; }; 
template <> struct valueGetter<int> { static constexpr auto &get = Func_IntValue; }; 
// etc. 

Теперь List_To_Vector становится тривиальным:

template <typename T> 
std::vector<T> List_To_Vector(Object& list) 
{ 
    std::vector<T> vec; 
    int listSize = Func_Size(list);  
    for (int i = 0; i < listSize; ++i) 
     vec.push_back(valueGetter<T>::get(Func_Next(list))); 

    return vec; 
} 

See it live on Coliru

0

Я предлагаю использовать помощник, чтобы вывести правильную перегрузку для преобразования:

class convert 
{ 
    const Object& from; 
public: 
    explicit convert(const Object& from): from(from) {} 

    operator char()  const { return Func_CharValue(from); } 
    operator bool()  const { return Func_BoolValue(from); } 
    operator std::string() const { return Func_StringValue(from); } 
    operator const char*() const { return Func_StringValue(from); } 
    // ... 
}; 

// ... 

vec.push_back(convert(arg)); 

Нет необходимости в шаблонах.

У этого есть недостаток в необходимости повторять каждый конкретный тип, даже если они используют одну и ту же функцию преобразования. Но у вас их не так много. Перегрузки могут быть дополнены оператором преобразования шаблонов, который по умолчанию отключен, но включен для типов, которые повторно используют общую функцию преобразования.

0

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

template<typename T> T object_to(const Object& arg) { } 

template<> int   object_to(const Object& arg) { return Func_IntValue(arg); } 
template<> unsigned int object_to(const Object& arg) { return Func_IntValue(arg); } 
template<> std::string object_to(const Object& arg) { return Func_StringValue(arg); } 
template<> float  object_to(const Object& arg) { return Func_DoubleValue(arg); } 
template<> double  object_to(const Object& arg) { return Func_DoubleValue(arg); } 
template<> bool   object_to(const Object& arg) { return Func_BoolValue(arg); } 
template<> char   object_to(const Object& arg) { return Func_CharValue(arg); } 

затем дать ваш класс Object некоторые стандартные методы алгоритма и подключить его в следующем:

1

Могу ли я играть слишком?

Я предлагаю решение, основанное на шаблоне удалены Convertion функции

template <typename T> 
T getTValue (T const &, Object const &) = delete; 

и некоторые функции преобразования не-шаблон, с той же подписью, для вызова правой Func_X_Value() функции; что-то вроде

int getTValue (int const &, Object const & obj) 
{ return Func_IntValue(arg); } 

unsigned int getTValue (unsigned int const &, Object const & obj) 
{ return Func_IntValue(arg); } 

float getTValue (float const &, Object const & obj) 
{ return Func_DoubleValue(arg); } 

double getTValue (double const &, Object const & obj) 
{ return Func_DoubleValue(arg); } 

char * getTValue (char const * &, Object const & obj) 
{ return Func_StringValue(arg); } 

std::string getTValue (std::string const &, Object const & obj) 
{ return Func_StringValue(arg); } 

char getTValue (char const &, Object const & obj) 
{ return Func_CharValue(arg); } 

bool getTValue (bool const &, Object const & obj) 
{ return Func_BoolValue(arg); } 

первый аргумент не используется и вводится только для выбора нужных функций не-шаблона, поэтому for цикл стал

for (int i = 0; i < listSize; ++i) 
    vec.push_back(getTValue(T(), arg)); 

Удаленная функция шаблон вводится, чтобы избежать преобразования нежелательных типов (на примере: от short int до int) и на этапе компиляции ввести ошибку, если кто-то попытается позвонить List_To_Vector() с неправильным T.

Так, к примеру, называют

std::vector<int> vi = List_To_Vector<int>(listContainingNumbers); 

должно быть в порядке, но назвать

std::vector<long> vl = List_To_Vector<long>(listContainingNumbers); 

потому getTValue<long>() удаляется и не существует функция getTValue(long const &, Object const &) не-шаблон.

p.s .: предостережения: код не проверен.

Смежные вопросы