Доброго дня,
Подскажите, как корректно реализовать следующее:
Есть некоторый список несвязанных между собой (без общего предка) типов 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;
}
Здравствуйте, 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>};
Что-то этот фрагмент тоже выглядит подозрительно ;)
Здравствуйте, 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>
Что-то этот фрагмент тоже выглядит подозрительно
Опечатка же.
Здравствуйте, SaZ, Вы писали:
SaZ>Как бы не помогает, пробовал уже. — https://ideone.com/24vOZu.
Тоже попробовал — помогает:
https://ideone.com/mQW8Sh
Кто-то не умеет делать Copy&Paste? Шутка. Но это всё же важный навык в программировании. У некоторых, говорят, таким методом получается всю программу целиком написать :)
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, SaZ, Вы писали:
SaZ>>Как бы не помогает, пробовал уже. — https://ideone.com/24vOZu.
W>Тоже попробовал — помогает: https://ideone.com/mQW8Sh
W>Кто-то не умеет делать Copy&Paste? Шутка. Но это всё же важный навык в программировании. У некоторых, говорят, таким методом получается всю программу целиком написать
Спасибо, поправился. А можете пояснить про typename T
= void и почему это помогло?
Здравствуйте, 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: Этот код — просто демонстрация, я не говорю, что так нужно писать :)
Здравствуйте, watchmaker, Вы писали:
W>...
W>Upd: Этот код — просто демонстрация, я не говорю, что так нужно писать
Спасибо, теперь всё понятно. Я понимал, что проблемы были как раз из-за отсутствия аргументов с вариадик типами, но не знал, как это разрешить.