Шаблон C ++ & quot; if constexpr & quot; в старый «Диспетчер тегов» метод

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

Моя цель состоит в том, чтобы иметь одну функцию синтаксического анализа, которая будет выполнять некоторые действия перед передачей данных в некоторые другие функции для выполнения конкретного анализа в зависимости от шаблона. T тип.
В прилагаемом коде это поведение, которое я хочу иметь. Я использую здесь 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

Решение

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

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);
}
3

Другие решения

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

// 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;
}
2

По вопросам рекламы [email protected]