Шаблон C ++ «if constexpr» в старый метод «Диспетчеризация тегов» - PullRequest
0 голосов
/ 23 октября 2018

Я довольно плохо знаком с шаблонными концепциями, такими как SFINAE или tag dispatching, и читал некоторые статьи и примеры, которые не помогли мне перейти к моему подходу.Поэтому я был бы очень признателен, если кто-то может помочь, пожалуйста.

Моя цель состоит в том, чтобы иметь одну-единственную функцию синтаксического анализа, которая будет выполнять некоторые действия перед передачей данных в другие функции для выполнения конкретного анализа в зависимости от шаблона T type.
В прикрепленном коде это поведение, которое я хочу иметь.Я использую здесь if constexpr, который, к сожалению, является функцией C ++ 17, которая недоступна в используемой мной версии C ++.
Я думаю, для этой цели это выглядит на первый взгляд, template specialization было бы лучшим решением, ноэто не то, что я хотел.
Я думаю, что для этой цели tag dispatching было бы хорошим направлением, но я не уверен, как именно это сделать с type_traits, когда у меня есть пользовательские типы, как всегда, у меня есть 2 варианта, true_type или false_type, но в следующем коде у меня есть 3 ситуации с потенциалом иметь больше.

Буду очень признателен за некоторые примеры или указания, пожалуйста, как лучше всего сделать то, что я ищу,Даже некоторую статью для чтения было бы здорово.

Заранее спасибо!

Пример рабочего кода:

#include <string>
#include <vector>
#include <memory>
using namespace std;

struct Base       { int id; };
struct Foo : Base { int fooValue; };
struct Bar : Base { int barValue; };

shared_ptr<Foo>         parseFoo(const string & data)  { return make_shared<Foo>(); }
shared_ptr<Bar>         parseBar(const string & data)  { return make_shared<Bar>(); }
shared_ptr<vector<Foo>> parseFoos(const string & data) { return make_shared<vector<Foo>>(); }

template <typename T>
shared_ptr<T> parse(const std::string & data)
{
    shared_ptr<T> result = nullptr;
    if (data.empty())
        return result;

    result = make_shared<T>();
    if constexpr      (std::is_same<T, Foo>::value)         result = parseFoo(data);
    else if constexpr (std::is_same<T, Bar>::value)         result = parseBar(data);
    else if constexpr (std::is_same<T, vector<Foo>>::value) result = parseFoos(data);

    return result;
}

int main()
{
    string data = "some json response";
    auto foo = parse<Foo>(data);
    auto bar = parse<Bar>(data);
    auto foos = parse<vector<Foo>>(data);

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Диспетчеризация тегов облегчила бы здесь:

struct Base       { int id; };
struct Foo : Base { int fooValue; };
struct Bar : Base { int barValue; };

template <typename T> struct Tag {};

std::shared_ptr<Foo> parse_impl(Tag<Foo>, const std::string& data)  { return make_shared<Foo>(); }
std::shared_ptr<Bar> parse_impl(Tag<Bar>, const std::string& data)  { return make_shared<Bar>(); }
std::shared_ptr<std::vector<Foo>> parse_impl(Tag<std::vector<Foo>>, const std::string& data)
{
    return make_shared<std::vector<Foo>>();
}

template <typename T>
std::shared_ptr<T> parse(const std::string& data)
{
    if (data.empty())
        return nullptr;
    return parse_impl(Tag<T>{}, data);
}
0 голосов
/ 23 октября 2018

Почему бы вам просто не предоставить шаблонные специализации для parseFoo, parseBar и parseFoos, а затем просто вызвать метод шаблона из статической функции parse:

//parseT replaces parseFoo, parseBar, parseFoos
template<typename T>
std::shared_ptr<T> parseT(const std::string & data); 

// provide implementaiton for Foo, Bar and vector<Foo>
template<>
std::shared_ptr<Foo> parseT<Foo>(const std::string & data)  { 
   return std::make_shared<Foo>(); 
}

template<>
std::shared_ptr<Bar> parseT<Bar>(const std::string & data)  { 
   return std::make_shared<Bar>(); 
}

template<>
std::shared_ptr<std::vector<Foo>> parseT<std::vector<Foo>>(const std::string & data)  { 
   return std::make_shared<std::vector<Foo>>(); 
}

template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
    std::shared_ptr<T> result = nullptr;
    if (data.empty())
        return result;

    result = std::make_shared<T>();
    result = parseT<T>(data); // simple call to template function

    return result;
}

РЕДАКТИРОВАТЬ: Ой, не читал должным образом, теперь я вижу, что это не то, что вы хотели (хотя не совсем уверен, почему, мне кажется, это лучший вариант: D).В любом случае, если вы хотите использовать теговую диспетчеризацию чего-либо в соответствии с приведенным ниже кодом, это приходит мне в голову (опять-таки, IMO не очень хорошо, из-за другого параметра шаблона для функции parser):

struct FooTag {};
struct BarTag{};
struct FoosTag{};

std::shared_ptr<Foo> parseT(const std::string & data, FooTag) {
    return std::make_shared<Foo>();
}

std::shared_ptr<Bar> parseT(const std::string & data, BarTag) {
    return std::make_shared<Bar>();
}

std::shared_ptr<std::vector<Foo>> parseT(const std::string & data, FoosTag) {
    return std::make_shared<std::vector<Foo>>();
}

// template version
template <typename T, typename Tag>
std::shared_ptr<T> parser(const std::string & data) {
    std::shared_ptr<T> result = nullptr;
    if (data.empty())
        return result;

    result = std::make_shared<T>();
    result = parseT(data, Tag());

    return result;
}

Если вам не нужен дополнительный параметр шаблона, вы можете позволить пользователю предоставить класс тега внутри Foo и Bar и так далее, но он не будет работать, если у вас vector из Foo s:

// Tag is now a nested class
class Foo {
    public:
    struct Tag{};

};
class Bar {
    public:
    struct Tag{};

};

std::shared_ptr<Foo> parseT(const std::string & data, Foo::Tag) {
    return std::make_shared<Foo>();
}

std::shared_ptr<Bar> parseT(const std::string & data, Bar::Tag) {
    return std::make_shared<Bar>();
}


template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
    std::shared_ptr<T> result = nullptr;
    if (data.empty())
        return result;

    result = std::make_shared<T>();
    result = parseT(data, T::Tag()); // tag is now inside of template parameter

    return result;
}

Еще один РЕДАКТИРОВАТЬ: вы можете сделать шаблон класса для тега, чтобы избавиться от дополнительного параметра шаблона в функции parser

template <typename T>
struct Tag{};

std::shared_ptr<Foo> parseT(const std::string & data, Tag<Foo>) {
    return std::make_shared<Foo>();
}

std::shared_ptr<Bar> parseT(const std::string & data, Tag<Bar>) {
    return std::make_shared<Bar>();
}


template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
    std::shared_ptr<T> result = nullptr;
    if (data.empty())
        return result;

    result = std::make_shared<T>();
    result = parseT(data, Tag<T>{});

    return result;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...