Я довольно новичок в шаблонных концепциях, таких как 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;
}
Диспетчеризация тегов облегчила бы здесь:
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);
}
Почему бы вам просто не предоставить шаблоны специализаций для 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;
}