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

Моя цель состоит в том, чтобы иметь одну единственную функцию синтаксического анализа, которая будет делать некоторые вещи перед передачей данных другим функциям для выполнения определенного анализа в зависимости от типа шаблона T.
В прилагаемом коде я хочу добиться именно такого поведения. Я использую здесь if constexpr, который, к сожалению, является функцией C ++ 17, которая недоступна в версии C ++, которую я использую.
Я думаю, что для этой цели на первый взгляд кажется, что template specialization было бы лучшим решением, но это не то, что я хотел.
Я думаю, что для этой цели tag dispatching было бы хорошим направлением, но я не уверен, как именно это сделать с type_traits, когда у меня есть пользовательские типы, поскольку у меня всегда есть 2 варианта, { {X5}} или 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
user7867434 23 Окт 2018 в 13:21

2 ответа

Лучший ответ

Отправка тегов здесь упростит:

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);
}
2
Jarod42 23 Окт 2018 в 11:59

Почему бы вам просто не указать специализацию шаблона для 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 {{ X3 }} с :

// 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;
}
1
Mike van Dyke 23 Окт 2018 в 12:31
52946652