Как можно отказаться от шаблонов?
От: SaZ  
Дата: 19.09.17 15:07
Оценка:
Здравствуйте,

Есть несколько структур, которые наследуются от базовой и содержат одинаковые поля. И некоторый враппер, который позволяет брать из них данные в удобном виде.
Хочется как-то отказаться от шаблонов, чтобы закоментированный код работал. Выносить общие переменные в базовую структуру нельзя.

В крайнем случае придётся делать промежуточную структуру и копировать все данные туда. Но очень не хочется. Как можно провернуть такой фокус?

Пример

#include <type_traits>
#include <string>
#include <iostream>
 
// Данные
struct Base
{};
 
struct A : Base
{
    std::string name = "A";
};
 
struct B : Base
{
    std::string name = "B";
};
 
// Геттеры
template< typename T >
struct Meta
{
    static_assert(std::is_base_of<Base, T>::value, "");
 
    public:
    explicit Meta( const T& data )
        : _data{ data }
    {}
 
    std::string name() const
    {
        return "Name is: " + _data.name;
    }
 
    const T* operator->() const
    {
        return &_data;
    }
 
private:
    const T& _data;
};
 
template< typename T >
Meta<T> Wrap( const T& data )
{
    return Meta<T>{ data };
}
 
 
int main()
{
    const A a;
    const B b;
    const auto wa = Wrap( a );
    const auto wb = Wrap( b );
 
    std::cout << wa.name() << "\n";
    std::cout << wb.name() << "\n";
 
    //for ( const auto& meta : { wa, wb } ) // Увы
    //    std::cout << meta.name() << "\n";
 
    return 0;
}
Отредактировано 19.09.2017 15:09 SaZ . Предыдущая версия .
Re: Как можно отказаться от шаблонов?
От: Alexander G Украина  
Дата: 19.09.17 15:34
Оценка: 5 (1) +2
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте,


SaZ>Есть несколько структур, которые наследуются от базовой и содержат одинаковые поля. И некоторый враппер, который позволяет брать из них данные в удобном виде.

SaZ>Хочется как-то отказаться от шаблонов, чтобы закоментированный код работал. Выносить общие переменные в базовую структуру нельзя.

Чтобы код, аналогичный закомментриованному, работал, не нужно отказываться от шаблона, нужно просто скрестить статический и динамический полиморфизм во таким образом:


struct MetaBase
{
    virtual ~MetaBase() {}
    virtual std::string name() const = 0;
 
    virtual const Base* operator->() const = 0;
};
 

 
// Геттеры
template< typename T >
struct Meta : MetaBase
{
    static_assert(std::is_base_of<Base, T>::value, "");
 
    public:
    explicit Meta( const T& data )
        : _data{ data }
    {}
 
    virtual std::string name() const override
    {
        return "Name is: " + _data.name;
    }
 
    virtual const T* operator->() const override
    {
        return &_data;
    }
 
private:
    const T& _data;
};
 

 
template< typename T >
std::unique_ptr<MetaBase> Wrap( const T& data )
{
    return std::make_unique<Meta<T>>( data );
}
Русский военный корабль идёт ко дну!
Re: type erasure
От: watchmaker  
Дата: 19.09.17 15:38
Оценка: +1
Здравствуйте, SaZ, Вы писали:

SaZ>Хочется как-то отказаться от шаблонов, чтобы закоментированный код работал. Выносить общие переменные в базовую структуру нельзя.

SaZ> Как можно провернуть такой фокус?

Динамический полиморфизм и type erasue всегда спасёт. Сделай базовый класс для Meta с виртуальной функцией name() и определяй её в потомках как угодно. Тогда итерироваться в цикле можно уже по ссылкам на этот базовый класс, которые все одного типа.

Либо можно даже без базового класса и виртуальности аналог написать: https://ideone.com/jJkd8N
Re[2]: type erasure
От: so5team https://stiffstream.com
Дата: 19.09.17 16:12
Оценка: 6 (1) +1
Здравствуйте, watchmaker, Вы писали:

W>Либо можно даже без базового класса и виртуальности аналог написать: https://ideone.com/jJkd8N


В данном случае можно обойтись и без std::function, посредством голых указателей: https://ideone.com/HPF1JI
Re[2]: Как можно отказаться от шаблонов?
От: night beast СССР  
Дата: 19.09.17 16:43
Оценка: +2
Здравствуйте, Alexander G, Вы писали:

SaZ>>Есть несколько структур, которые наследуются от базовой и содержат одинаковые поля. И некоторый враппер, который позволяет брать из них данные в удобном виде.

SaZ>>Хочется как-то отказаться от шаблонов, чтобы закоментированный код работал. Выносить общие переменные в базовую структуру нельзя.

AG>Чтобы код, аналогичный закомментриованному, работал, не нужно отказываться от шаблона, нужно просто скрестить статический и динамический полиморфизм во таким образом:


для данного примера можно без уника:
const MetaBase& wa = Wrap( a );
const MetaBase& wb = Wrap( b );
 
std::cout << wa.name() << "\n";
std::cout << wb.name() << "\n";
 
for ( const auto meta : { &wa, &wb } )
    std::cout << meta->name() << "\n";
Re[3]: type erasure
От: SaZ  
Дата: 20.09.17 14:35
Оценка:
Здравствуйте, so5team, Вы писали:

S>В данном случае можно обойтись и без std::function, посредством голых указателей: https://ideone.com/HPF1JI


Спасибо, как-то так и хотелось.
Re: Как можно отказаться от шаблонов?
От: _niko_ Россия  
Дата: 20.09.17 14:40
Оценка: 6 (1)
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте,


SaZ>Есть несколько структур, которые наследуются от базовой и содержат одинаковые поля. И некоторый враппер, который позволяет брать из них данные в удобном виде.

SaZ>Хочется как-то отказаться от шаблонов, чтобы закоментированный код работал. Выносить общие переменные в базовую структуру нельзя.

SaZ>В крайнем случае придётся делать промежуточную структуру и копировать все данные туда. Но очень не хочется. Как можно провернуть такой фокус?


SaZ>Пример


Как вариант без копирования данных и без виртуальности...
На ideone

#include <type_traits>
#include <string>
#include <iostream>

// Данные
struct Base
{};

struct A: Base
{
    std::string name = "name_A";
};

struct B: Base
{
    std::string name = "name_B";
    std::string description = "desc_B";
};

struct C: Base
{
    std::string description = "desc_C";
};

class MetaAll;


class MetaName
{
public:

    inline const std::string& name() const { return *_name; }

public:

    MetaName()
        : _name{&_const_name_empty}
    {};

    template<
        class T,
        std::enable_if_t<!std::is_base_of<MetaAll, T>::value>* = nullptr // for VC
    >
    explicit MetaName(const T& t)
        : _name{&t.name}
    {}

    MetaName(const MetaName&) = default;
    MetaName& operator=(const MetaName&) = default;

private:

    static const std::string _const_name_empty;
    const std::string* _name;
};
const std::string MetaName::_const_name_empty;


class MetaDescription
{
public:

    inline const std::string& description() const { return *_description; }

public:

    MetaDescription()
        : _description{&_const_description_empty}
    {};

    template<
        class T,
        std::enable_if_t<!std::is_base_of<MetaAll, T>::value>* = nullptr // for VC
    >
    explicit MetaDescription(const T& t)
        : _description{&t.description}
    {}

    MetaDescription(const MetaDescription&) = default;
    MetaDescription& operator=(const MetaDescription&) = default;

private:

    static const std::string _const_description_empty;
    const std::string* _description;
};
const std::string MetaDescription::_const_description_empty;




struct FieldNameTag {};
struct FieldDescriptionTag {};
const FieldNameTag FieldName;
const FieldDescriptionTag FieldDescription;


class MetaAll: public MetaName, public MetaDescription
{
public:

    MetaAll() = default;
    MetaAll(const MetaAll&) = default;
    MetaAll& operator=(const MetaAll&) = default;

    template<class T>
    MetaAll(const T& t, FieldNameTag)
        : MetaName(t)
        , MetaDescription()
    {}

    template<class T>
    MetaAll(const T& t, FieldDescriptionTag)
        : MetaName()
        , MetaDescription(t)
    {}

    template<class T>
    MetaAll(const T& t, FieldNameTag, FieldDescriptionTag)
        : MetaName(t)
        , MetaDescription(t)
    {}
};



int main()
{
    const A a;
    const B b;
    const C c;

    for (MetaName meta : {MetaName(a), MetaName(b)})
    {
        std::cout << meta.name() << std::endl;
    }

    for (MetaDescription meta : {MetaDescription(b), MetaDescription(c)})
    {
        std::cout << meta.description() << std::endl;
    }

    for (MetaAll meta : {MetaAll(a, FieldName), MetaAll(b, FieldName, FieldDescription), MetaAll(c, FieldDescription)})
    {
        std::cout << meta.name() << " <-> " << meta.description() << std::endl;
    }

    // MetaAll meta(a, FieldDescription); -> Compile time error

    return 0;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.