Как отличить контейнер от простого значения?
От: Аноним  
Дата: 12.10.07 11:48
Оценка:
Есть такой метод:

template <class _ValueType>
bool GetValue(const std::string& value_name, _ValueType& value);

_ValueType — может быть как простым значением так и контейнером.

реализация метода на псевдокоде примерно такая:
if (_ValueType контейнер)
{
std::vector< std::string > strValues = GetStrValues(value_name);
for (std::vector< std::string >::const_iterator it; ...)
{
value.push_back(FromString(*it));
}
}
else
{
std::string strValue = GetStrValue(value_name); //внутренний метод
value = FromString(strValue);
}

Подскажите, как определить _ValueType контейнер или нет???
Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.
Re: Как отличить контейнер от простого значения?
От: jazzer Россия Skype: enerjazzer
Дата: 12.10.07 12:13
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть такой метод:


А>template <class _ValueType>

А>bool GetValue(const std::string& value_name, _ValueType& value);

А>_ValueType — может быть как простым значением так и контейнером.


заведи traits, типа такого:
template <class T>
struct is_container
{
  enum { value = false };
};

и специализируй его для типов, которые являются контейнерами
template <>
struct is_container<std::string>
{
  enum { value = true };
};


Кстати, так как у всех STL-контейнеров определен тип value_type, то можно сделать что-нть вроде (в таком виде не скомпилируется, но идея, думаю, понятна):
template <class T>
struct is_container
{
  typedef typename T::value_type v;
  enum { value = true };
};

тогда по SFINAE для всех STL-контейнеров is_container<...>::value == true.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Как отличить контейнер от простого значения?
От: StevenIvanov США  
Дата: 12.10.07 12:16
Оценка:
Здравствуйте, Аноним, Вы писали:

А>...


как вариант?


template<typename ValueType>
void GetValue( long id, ValueType& value ) {}

template<typename ValueType>
void GetValue( long id, std::vector<ValueType>& value ) {}
Re: Как отличить контейнер от простого значения?
От: Bell Россия  
Дата: 12.10.07 12:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Подскажите, как определить _ValueType контейнер или нет???

Что известно о контейнере кроме того, что он должен иметь функцию push_back ?

А>Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.

Вряд ли удастся совместить в одной функции строки
value.push_back(FromString(*it));
value = FromString(strValue);
Любите книгу — источник знаний (с) М.Горький
Re[2]: Как отличить контейнер от простого значения?
От: Аноним  
Дата: 12.10.07 13:31
Оценка:
SI>
SI>template<typename ValueType>
SI>void GetValue( long id, ValueType& value ) {}

SI>template<typename ValueType>
SI>void GetValue( long id, std::vector<ValueType>& value ) {}
SI>


SI>как вариант?


Вариант не подходит, т.к в качестве контейнера может быть еще deque (из-за проблем vector<bool>) или list. Обязательное условие для контейнера наличие метода push_back.
Re[2]: Как отличить контейнер от простого значения?
От: Аноним  
Дата: 12.10.07 13:45
Оценка:
Здравствуйте, Bell, Вы писали:

B>Здравствуйте, Аноним, Вы писали:


А>>Подскажите, как определить _ValueType контейнер или нет???

B>Что известно о контейнере кроме того, что он должен иметь функцию push_back ?
фактически в качестве контейнеров пока используются только list, deque и vector.
Кроме push_back аграничений пока нет.

А>>Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.

B>Вряд ли удастся совместить в одной функции строки
B>
B>value.push_back(FromString(*it));
B>value = FromString(strValue);
B>


Да, видимо придется специализировать для контейнеров. Но для каждого отдельного контейнера добавлять специализацию не охота...
Re[2]: Как отличить контейнер от простого значения?
От: zaufi Земля  
Дата: 12.10.07 13:59
Оценка:
можно я позанудствую чучуть

Здравствуйте, StevenIvanov, Вы писали:

template<typename ValueType>
void GetValue( long id, ValueType& value ) {}

template<typename ValueType, typename Allocator>
void GetValue( long id, std::vector<ValueType, Allocator>& value ) {}


---
а в целом по топику могу сказать что не знаю пока способа лучще чем сделать метафункцию is_std_container и явно специализировать ее для всех STL контейнеров -- благо их оч небольшое число

и тогда:

template <typename ValueType>
typename boost::enable_if<
    is_std_container<ValueType>
  > GetValue(long id, ValueType& value)
{
    // тут код для случая когда value является контейнером
}

template <typename ValueType>
typename boost::enable_if<
    boost::mpl::not_<is_std_container<ValueType> >
  > GetValue(long id, ValueType& value)
{
    // тут код для случая когда value не является конетйнером
}

какнить так
Re[3]: Как отличить контейнер от простого значения?
От: Erop Россия  
Дата: 12.10.07 14:09
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Да, видимо придется специализировать для контейнеров. Но для каждого отдельного контейнера добавлять специализацию не охота...



Ну заведи себе что-нибудь типа
template<typename TContainer> struct ContainerRef { 
    TContainer& Ref; 
    ContainerRef( TContainer& ref ) : Ref( ref ) {}
};

template<typename T> T& tryGetAsContainer( T& t ) { return t; }
template<typename T> ContainerRef< std::vector<T> > tryGetAsContainer( std::vector<T>& t ) { return t; }
template<typename T> ContainerRef< std::list<T> > tryGetAsContainer( std::list<T>& t ) { return t; }
// и т. д.

Ну и пишешь дальше:
template<typename T>void doMyFunction( T& t ) { /* реализация для скаляра*/ }
template<typename TContainer>void doMyFunction( ContainerRef< TContainer > t ) { /* реализация для контейнера*/ }
template<typename T> void MyFunction( T& t ) { doMyFunction( tryGetAsContainer( t ) ); }


Ну или с тритсами химичишь, как тут уже и писали...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Как отличить контейнер от простого значения?
От: Bell Россия  
Дата: 12.10.07 14:15
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Да, видимо придется специализировать для контейнеров. Но для каждого отдельного контейнера добавлять специализацию не охота...


Можно замутить трейт для стандартных контейнеров на базе value_type, как предложил jazzer:

template<class T>
class is_std_container
{
   template <class U>
   static char   test(typename U::value_type*);
   template <class U>
   static double test(...);
public:
   enum { val = (1 == sizeof(test<T>(0)) ? 1 : 0) };
};

//Исключаем std::basic_string из списка контейнеров
template<class T>
class is_std_container<std::basic_string<T> >
{
public:
   enum { val = 0 };
};

int main()
{
   cout << is_std_container<int>::val << '\n';
   cout << is_std_container<double>::val << '\n';
   cout << is_std_container<CString>::val << '\n';
   cout << is_std_container<string>::val << '\n';
   cout << is_std_container<vector<string> >::val << '\n';
   return 0;
}
Любите книгу — источник знаний (с) М.Горький
Re: Как отличить контейнер от простого значения?
От: Аноним  
Дата: 12.10.07 14:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Подскажите, как определить _ValueType контейнер или нет???

А>Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.

Настоятельно советую посмотреть в сторону паттерна Composite (см. описание у GoF). он унифицирует обработку элемента, независимо от того, одиночный это объект или коллекция.
Re[4]: Как отличить контейнер от простого значения?
От: Quasi  
Дата: 12.10.07 17:50
Оценка:
Здравствуйте, Bell, Вы писали:

B>
B>template<class T>
B>class is_std_container
B>{
B>   template <class U>
B>   static char   test(typename U::value_type*);
B>   template <class U>
B>   static double test(...);
B>public:
B>   enum { val = (1 == sizeof(test<T>(0)) ? 1 : 0) };
B>};

B>//Исключаем std::basic_string из списка контейнеров
B>template<class T>
B>class is_std_container<std::basic_string<T> >
B>{
B>public:
B>   enum { val = 0 };
B>};
B>

— фактически реализация BOOST_MPL_HAS_XXX_TRAIT_DEF

Я бы еще сделал так:
BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);

template <typename T, bool>
struct std_container_check
{
    static const bool result = false;
};

template <typename T>
struct std_container_check<T, true>
{
    static const bool result = true;
    BOOST_CLASS_REQUIRE(T, boost, BackInsertionSequenceConcept);
};

template<class T>
struct is_std_container
{
    static const bool result = std_container_check<T, has_value_type<T>::value >::result;
};

struct test
{
    typedef int value_type;
};

int main()
{

    
    std::cout << is_std_container<std::vector<int> >::result << "\n"
          << is_std_container<int>::result << "\n";
          //<< is_std_container<test>::result << "\n"; А вот это уже по крайней мере не скомпилится
        return 0;
}
Re: Как отличить контейнер от простого значения?
От: Roman Odaisky Украина  
Дата: 12.10.07 18:40
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.


А надо, это единственно правильный вариант.
До последнего не верил в пирамиду Лебедева.
Re: Как отличить контейнер от простого значения?
От: ILva_ Россия  
Дата: 12.10.07 20:13
Оценка:
А>Подскажите, как определить _ValueType контейнер или нет???
А>Понятно, что этот метод можно разбить на 2, GetValue и GetValues... но не хочется.

Как уже писал Roman Odaisky, нужно сделать 2 разных метода. Повод для этого конструкция if(){}else{}.
В обобщенном виде можно так:
template<class ValueType>
bool GetValue(const std::string& name, ValueType& value);
template<class ContainerType>
bool GetValues(const std::string& name, ContainerType& values);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.