Помогите с вариадиками
От: SaZ  
Дата: 09.01.18 13:25
Оценка:
Доброго дня,

Подскажите, как корректно реализовать следующее:
Есть некоторый список несвязанных между собой (без общего предка) типов T1, T2, T3..., у каждого из которых есть статический метод StaticMeta.
Нужно написать функцию bool Validate<T1, T2, T3>(const MetaInfo& staticMeta) которая сравнит staticMeta с каждым из типов (staticMeta==T1::StaticMeta()).

Что-то не получается сделать нужные перегрузки. Пример кода (немного сложноват, но отражает задачу)
#include <iostream>
#include <string>
#include <memory>
#include <utility>
#include <cassert>
 
#define GENERATE_META(type)                 \
    public:                                 \
    static const MetaInfo& StaticMeta()     \
    {                                       \
        static MetaInfo meta{#type}; \
        return meta;                        \
    }
 
struct MetaInfo
{
    const std::string _id;
 
    explicit MetaInfo( std::string ti )
        : _id{std::move( ti )}{}
 
    bool operator==(const MetaInfo& rhs) const
    {
        return _id == rhs._id;
    }
 
    bool operator!=(const MetaInfo& rhs) const
    {
        return operator==(rhs);
    }
};
 
// Inheritance is just for example
// In real project code is generated by tools
struct Base
{
protected:
    Base() = default;
};
 
struct MyType1 : Base
{
    GENERATE_META(MyType1)
    int _dummy = 1;
};
 
struct MyType2 : Base
{
    GENERATE_META(MyType2)
    float _dummy = 2.f;
};
 
struct MyType3 : Base
{
    GENERATE_META(MyType3)
    std::string _dummy = "gg";
};
 
 
class Table
{
public:
    template< typename T >
    static Table Create()
    {
        auto data = std::make_shared<T>();
        auto meta = &T::StaticMeta();
        return { data, meta };
    }
 
    const std::shared_ptr<Base>& Data() const{ return _data; }
    const MetaInfo& Meta() const{ return *_meta; }
 
private:
    Table( std::shared_ptr<Base> data, const MetaInfo * meta )
        : _data{std::move(data)}
        , _meta{meta}
    {}
 
private:
    std::shared_ptr<Base> _data;
    const MetaInfo * const _meta = nullptr;
};
 
template< typename T >
void Print( const Table& table )
{
    assert( table.Meta() == T::StaticMeta() );
    const auto& data = *static_cast<T*>(table.Data().get());
    std::cout << table.Meta()._id << " -> " << data._dummy << "\n";
}
 
/////////////////////////////////////////////////////////////////////
 
template< typename T >
bool Validate( const Table& table )
{
    if ( table.Meta() == T::StaticMeta() )
    {
        Print<T>( table );
        return true;
    }
 
    return false;
}
 
template< typename T, typename ...Tables >
bool Validate( const Table& table )
{
    if ( Validate<T>( table ) )
        return true;
 
    return Validate<Tables...>(table);
}
 
 
int main()
{
    const auto t1 = Table::Create<MyType1>();
    const auto t2 = Table::Create<MyType2>();
    const auto t3 = Table::Create<MyType3>();
 
    const auto r1 = Validate<MyType1, MyType2>(t1);
    assert( r1 );
    const auto r2 = Validate<MyType1, MyType2>(t2);
    assert( r2 );
    const auto r3 = Validate<MyType1, MyType2>(t3);
    assert( !r3 );
 
    return 0;
}
Re: Помогите с вариадиками
От: watchmaker  
Дата: 09.01.18 13:55
Оценка: 4 (1) +1
Здравствуйте, SaZ, Вы писали:

SaZ>Что-то не получается сделать нужные перегрузки.


А зачем вообще тут делать перегрузки? Они же лишние и пользы не несут. Просто переименуй первый метод Validate и всё получится:

// проверка одного элемента

template< typename T >
bool Validate_1( const Table& table )
{
    if ( table.Meta() == T::StaticMeta() )
    {
        Print<T>( table );
        return true;
    }

    return false;
}

// проверка списка

template< typename T = void, typename ...Tables >
bool Validate( const Table& table )
{
    if ( Validate_1<T>( table ) )
        return true;

    return Validate<Tables...>(table);
}

template<>
bool Validate( const Table& table ) {
  return false;
}

Как бы тут сразу видно, что функции для проверки одного элемента и для проверки списка из одного элемента — разные. Ибо список из одного элемента и сам элемент — тоже вроде разные типы.




SaZ>    bool operator!=(const MetaInfo& rhs) const
SaZ>    {
SaZ>        return operator==(rhs);
SaZ>    }
SaZ>};
Что-то этот фрагмент тоже выглядит подозрительно ;)
Re[2]: Помогите с вариадиками
От: SaZ  
Дата: 09.01.18 14:06
Оценка:
Здравствуйте, watchmaker, Вы писали:

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


SaZ>>Что-то не получается сделать нужные перегрузки.


W>А зачем вообще тут делать перегрузки? Они же лишние и пользы не несут. Просто переименуй первый метод Validate и всё получится:

  Скрытый текст
W>// проверка одного элемента
W>template< typename T >
W>bool Validate_1( const Table& table )
W>{
W>    if ( table.Meta() == T::StaticMeta() )
W>    {
W>        Print<T>( table );
W>        return true;
W>    }

W>    return false;
W>}

W>// проверка списка

W>template< typename T = void, typename ...Tables >
W>bool Validate( const Table& table )
W>{
W>    if ( Validate_1<T>( table ) )
W>        return true;

W>    return Validate<Tables...>(table);
W>}

W>template<>
W>bool Validate( const Table& table ) {
W>  return false;
W>}

W>Как бы тут сразу видно, что функции для проверки одного элемента и для проверки списка из одного элемента — разные. Ибо список из одного элемента и сам элемент — тоже вроде разные типы.

Как бы не помогает, пробовал уже. — https://ideone.com/24vOZu.

W>
SaZ>>    bool operator!=(const MetaInfo& rhs) const
SaZ>>    {
SaZ>>        return operator==(rhs);
SaZ>>    }
SaZ>>};
W>
Что-то этот фрагмент тоже выглядит подозрительно

Опечатка же.
Отредактировано 09.01.2018 14:08 SaZ . Предыдущая версия . Еще …
Отредактировано 09.01.2018 14:06 SaZ . Предыдущая версия .
Re[3]: Помогите с вариадиками
От: watchmaker  
Дата: 09.01.18 14:12
Оценка: 4 (1)
Здравствуйте, SaZ, Вы писали:

SaZ>Как бы не помогает, пробовал уже. — https://ideone.com/24vOZu.


Тоже попробовал — помогает: https://ideone.com/mQW8Sh
Кто-то не умеет делать Copy&Paste? Шутка. Но это всё же важный навык в программировании. У некоторых, говорят, таким методом получается всю программу целиком написать :)
Re[4]: Помогите с вариадиками
От: SaZ  
Дата: 09.01.18 14:17
Оценка:
Здравствуйте, watchmaker, Вы писали:

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


SaZ>>Как бы не помогает, пробовал уже. — https://ideone.com/24vOZu.


W>Тоже попробовал — помогает: https://ideone.com/mQW8Sh

W>Кто-то не умеет делать Copy&Paste? Шутка. Но это всё же важный навык в программировании. У некоторых, говорят, таким методом получается всю программу целиком написать

Спасибо, поправился. А можете пояснить про typename T = void и почему это помогло?
Re[5]: Помогите с вариадиками
От: watchmaker  
Дата: 09.01.18 18:14
Оценка: 6 (1)
Здравствуйте, SaZ, Вы писали:

SaZ> А можете пояснить про typename T = void и почему это помогло?


Это не самая важная часть: можно и без этого.
Указания параметра по умолчанию =void используется просто чтобы задать условие окончания через специализацию (а она для функций возможна только полная).

Чаще, конечно, окончание рекурсии делается через подсчёт параметров и ту же перегрузку. Но тут у тебя все функции имеют ровно один и тот же список аргументов (const Table& table), и для них этот способ закрыт.

Хотя можно это обойти, если ввести в сигнатуру функции эти типы, добавив, например, фиктивный аргумент.
Тогда у всех функций будут разные сигнатуры и перегрузка будет работать:
template <typename... Types>
using TupleTypeList = std::tuple<Types*...>*; // сделаем список на основе существующего std::tuple, чтобы не писать для примера свой класс

bool ValidateTuple(const Table& table, TupleTypeList<>) { // перегрузка для пустого списка типов
    return false;
}

template <typename Head, typename ...Tail>
bool ValidateTuple(const Table& table, TupleTypeList<Head, Tail...>)  // разбор общего случая с непустым списком типов
{
    if (Validate_1<Head>(table))
      return true;
    return ValidateTuple(table, TupleTypeList<Tail...>{});
}

template <typename ...Tables> 
bool Validate(const Table& table) {
    return ValidateTuple(table, TupleTypeList<Tables...>{});
}

Тут просто передаётся нулевой указатель (оптимизатор его выбросит потом) на кортеж, параметры которого и являются тем списком классов, который осталось проверить.

Upd: Этот код — просто демонстрация, я не говорю, что так нужно писать :)
Отредактировано 10.01.2018 13:11 watchmaker . Предыдущая версия . Еще …
Отредактировано 09.01.2018 18:22 watchmaker . Предыдущая версия .
Re[6]: Помогите с вариадиками
От: SaZ  
Дата: 10.01.18 08:12
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>...

W>Upd: Этот код — просто демонстрация, я не говорю, что так нужно писать

Спасибо, теперь всё понятно. Я понимал, что проблемы были как раз из-за отсутствия аргументов с вариадик типами, но не знал, как это разрешить.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.