我有一个使用其他许多类作为模板参数的类(如果需要的话,可以使用TAO PEGTL进行编译时语法生成),并且想知道是否有更好,更可扩展的方法来做到这一点,比手动输入所有内容。
当前情况:
//Class1.h
class Class1 {}
...
//ClassN.h
class ClassN {}
//Collection.h
struct collectionClass : templateClass<Class1,...,ClassN>
//SpecificTemplate.h
template<> struct specificClass<Class1>{
//Do the same
}
...
template<> struct specificClass<ClassN>{
//Do the same
}
当前,这必须手动完成(对于不同的“收藏”类,必须在多个位置进行)。
是否可以将其更改为更好的可管理替代方式,例如:
所需情况:
//Class1.h
class Class1 {}
REGISTER_CLASS(Class1)
...
//ClassN.h
class ClassN {}
REGISTER_CLASS(ClassN)
//Collection.h
struct collectionClass : templateClass<REGISTERED_CLASSES>
//SpecificTemplate.h
CREATE_CLASSES_FROM_REGISTERED()
最近几天,我一直在尝试通过提高PP和MPL来实现这一目标,但我不确定是否有可能。
编辑:
pegtl需要特定实例,如下所示: 有一些预定义为的动作:
template<typename Rule>
struct Action : tao::pegtl::nothing<Rule> {
};
并且必须实例化为:
template<>
struct Action<specificRule> {
static void apply0(State &state) {
state.rule = specificRule::ID;
}
};
答案 0 :(得分:3)
我建议在代码库的单个位置上显式手动注册所有“规则”(Class1
,...,ClassN
):
// foo_rule.hpp
#pragma once
struct FooRule {};
// bar_rule.hpp
#pragma once
struct BarRule {};
// foobar_rule.hpp
#pragma once
struct FoobarRule {};
// registered_rules.hpp
#pragma once
#include <tuple>
#include "foo_rule.hpp"
#include "bar_rule.hpp"
#include "foobar_rule.hpp"
using RegisteredRules = std::tuple<FooRule, BarRule, FoobarRule>;
上面的机制对于任何阅读该代码的人来说都是显而易见的:我们可以完全确定哪些规则已注册。
显然,缺点是规则定义和规则注册的分离:添加名为SuperRule
的新规则需要两个步骤:
struct SuperRule{};
SuperRule
附加到“ registered_rules.hpp”中RegisteredRules
的列表中。显然有忘记步骤2的危险。如果您愿意,可以发明一种防止此错误的机制,但让我们集中精力解决问题的其余部分。
您正在寻求生成此代码的策略:
struct FirstCollection : TemplateClass<FooRule, BarRule/*, ...*/> {};
struct SecondCollection : TemplateClass<FooRule, BarRule/*, ...*/> {};
// where /*, ...*/ refers to all remaining rules which have been registered
让我们为此使用称为rewrap
的原语。然后,生成上述继承的代码读取
struct FirstCollection : rewrap<RegisteredRules, TemplateClass> {};
struct SecondCollection : rewrap<RegisteredRules, TemplateClass> {};
很显然,rewrap
应该将其第一个输入的可变参数类型参数“注入”到其第二个输入:
template<class OldWrapped, template<class...> class NewWrapper>
using rewrap = /* to be implemented */
static_assert(
std::is_same<
rewrap<std::pair<int, double>, std::tuple>,
std::tuple<int, double>
>{}
);
static_assert(
std::is_same<
rewrap<std::tuple<char, long>, std::pair>,
std::pair<char, long>
>{}
);
Action
在您的问题中,您询问如何为所有已注册规则专业化模板类Action
:
template<>
struct Action<FooRule>{
static void apply0(State& state) {
// do the same
}
}
/*...*/
template<>
struct Action<FoobarRule>{
static void apply0(State& state) {
// do the same
}
}
相反,我建议使用部分专业化。让我们假设您被允许向Action
添加第二个模板参数:
template<class Rule>
struct Nothing {
static void apply0(State&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<
class Rule,
class Enable = void
> struct Action
: Nothing<Rule>
{};
第二个模板参数可用于玩普通的SFINAE游戏:
template<
class SpecificRule
> struct Action<
SpecificRule,
std::enable_if_t<
is_wrapped_in<SpecificRule, RegisteredRules>// to be implemented
>
> {
static void apply0(State&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
您显然需要的是另一个称为is_wrapped_in
的原语。
#include <iostream>
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
////////////////////////////////////////////////////////////////////////////////
// rewrap
namespace detail {
template<
class OldWrapped,
template<class...> class NewWrapper
> struct Rewrap;
template<
template<class...> class OldWrapper,
class... Wrappees,
template<class...> class NewWrapper
> struct Rewrap<
OldWrapper<Wrappees...>,
NewWrapper
> {
using T = NewWrapper<Wrappees...>;
};
}// detail
template<class OldWrapped, template<class...> class NewWrapper>
using rewrap = typename detail::Rewrap<OldWrapped, NewWrapper>::T;
static_assert(
std::is_same<
rewrap<std::pair<int, double>, std::tuple>,
std::tuple<int, double>
>{}
);
static_assert(
std::is_same<
rewrap<std::tuple<char, long>, std::pair>,
std::pair<char, long>
>{}
);
////////////////////////////////////////////////////////////////////////////////
// is_wrapped_in
namespace detail {
template<class T, class Wrapped>
struct IsWrappedIn;
template<class T, template<class...> class Wrapper, class... Wrappees>
struct IsWrappedIn<T, Wrapper<Wrappees...>>
: std::bool_constant<(... || std::is_same<T, Wrappees>{})>
{};
}// detail
template<class T, class Wrapped>
constexpr bool is_wrapped_in = detail::IsWrappedIn<T, Wrapped>::value;
static_assert(is_wrapped_in<int, std::tuple<char, char, int, long>> == true);
static_assert(is_wrapped_in<int, std::tuple<char, char, long, long>> == false);
static_assert(is_wrapped_in<int, std::pair<int, int>> == true);
////////////////////////////////////////////////////////////////////////////////
// registered_rules
struct UnregisteredRule {};
struct FooRule {};
struct BarRule {};
struct FoobarRule {};
using RegisteredRules = std::tuple<FooRule, BarRule, FoobarRule>;
////////////////////////////////////////////////////////////////////////////////
// collections
template<class... Rules>
struct TemplateClass {
using Root = TemplateClass<Rules...>;// convenience alias for derived classes
};
struct FirstCollection : rewrap<RegisteredRules, TemplateClass> {};
struct SecondCollection : rewrap<RegisteredRules, TemplateClass> {};
static_assert(
std::is_same<
FirstCollection::Root,
TemplateClass<FooRule, BarRule, FoobarRule>
>{}
);
static_assert(
std::is_same<
SecondCollection::Root,
TemplateClass<FooRule, BarRule, FoobarRule>
>{}
);
////////////////////////////////////////////////////////////////////////////////
// action
struct State {};
template<class Rule>
struct Nothing {
static void apply0(State&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<
class Rule,
class Enable = void
> struct Action
: Nothing<Rule>
{};
template<
class SpecificRule
> struct Action<
SpecificRule,
std::enable_if_t<
is_wrapped_in<SpecificRule, RegisteredRules>
>
> {
static void apply0(State&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
////////////////////////////////////////////////////////////////////////////////
int main() {
State state{};
Action<UnregisteredRule>::apply0(state);
Action<FooRule>::apply0(state);
Action<BarRule>::apply0(state);
Action<FoobarRule>::apply0(state);
}
使用GCC 8.2.0的输出:
static void Nothing<Rule>::apply0(State&) [with Rule = UnregisteredRule]
static void Action<SpecificRule, typename std::enable_if<is_wrapped_in<SpecificRule, std::tuple<FooRule, BarRule, FoobarRule> >, void>::type>::apply0(State&) [with SpecificRule = FooRule]
static void Action<SpecificRule, typename std::enable_if<is_wrapped_in<SpecificRule, std::tuple<FooRule, BarRule, FoobarRule> >, void>::type>::apply0(State&) [with SpecificRule = BarRule]
static void Action<SpecificRule, typename std::enable_if<is_wrapped_in<SpecificRule, std::tuple<FooRule, BarRule, FoobarRule> >, void>::type>::apply0(State&) [with SpecificRule = FoobarRule]
答案 1 :(得分:0)
我不知道您可以更改多少代码,但是...如果您可以数字对类Class1
,Class2
,... {{1 }}使它们专门化模板类ClassN
ClassX
以使// class_base.h (additional header)
template <std::size_t>
struct ClassX;
成为Class1
,ClassX<1u>
成为Class2
,等等
ClassX<2u>
您可以进行设置,以便您的// class1.h
template <>
struct ClassX<1u>
{ };
// class2.h
template <>
struct ClassX<2u>
{ };
// a file for ClassX<3>, one for ClassX<4>, etc
可以继承所有collectionclass
专业化类别-如果它们的编号是连续的,并且您注册了classX
专业化类别的数量-使用一个类帮助器classX
和std::index_sequence
(从C ++ 14开始可用)
std::make_index_sequence
现在,在specifictemplate.h中,您只需编写即可(如果我理解正确,并且所有static constexpr std::size_t numOfClasses { 2u }; // num of ClassX classes
template <typename...>
struct templateClass
{ };
template <typename>
struct cc_helper;
template <std::size_t ... Is>
struct cc_helper<std::index_sequence<Is...>>
: public templateClass<ClassX<Is+1u>...> // +1u if start from `ClassX<1u>
{ }; // without +1u is start from
// ClassX<0u>
struct collectionclass
: public cc_helper<std::make_index_sequence<numOfClasses>>
{ };
都相等的话)
specificClass
答案 2 :(得分:0)
您可以使用函数重载的巧妙技巧,以及在执行上述重载时派生类型的优先级高于其基本类型的事实。
这将使您能够编写:
TYPE_COLLECTOR_START(foo);
TYPE_COLLECTOR_ADD(foo, int);
TYPE_COLLECTOR_ADD(foo, std::pair<bool, bool>);
TYPE_COLLECTOR_ADD(foo, char);
TYPE_COLLECTOR_END(foo);
static_assert(
std::is_same_v<foo, type_collector::list<int, std::pair<bool, bool>, char>>
);
此技巧不符合标准,因为它使用__COUNTER__
编译器扩展宏,但几乎所有编译器都对其进行了定义。
基本上,您定义了一个version<N>
类,该类从version<N - 1>
继承(递归):
template <unsigned long N> struct version : version<N - 1> {};
template <> struct version<0> {};
然后使用version
定义一个函数重载列表,其中函数重载的数字__COUNTER__
不断增加:
// the result is stored in this type
template <class... Ts>
struct list {
template <class T>
using add = list<Ts..., T>;
};
// macro TYPE_COLLECTOR_START usage will expand to
auto get(version<0>) -> list<>;
// macro TYPE_COLLECTOR_ADD usages will expand to
auto get(version<1>) -> decltype(get(version<0>{}))::add<int>;
auto get(version<2>) -> decltype(get(version<1>{}))::add<std::pair<bool, bool>>;
auto get(version<3>) -> decltype(get(version<2>{}))::add<char>;
// macro TYPE_COLLECTOR_END usage will expand to
using foo = decltype(get(version<3>{}));
很显然,您希望避免名称冲突(并可能将类型同时收集到两个不同的列表中;并且要进行交错处理),因此我编写了一个更详尽的示例供参考。
对于您的特定用例,如果我对您的理解是正确的,则可以在我上面提供的内容上定义包装宏,这些宏既可以将类型添加到列表中,也可以定义专业化:
#define REGISTER_RULE(rule)\
TYPE_COLLECTOR_ADD(registered_rules, rule)\
\
template <>\
struct Action<rule_name> {\
static void apply0(State& state) {\
state.rule = rule_name::ID;\
}\
}
然后您将得到类似的内容:
// registered_rules.h
namespace tao_pegtl_namespace { TYPE_COLLECTOR_START(registered_rules); }
// rule1.h
#include "registered_rules.h"
namespace your_namespace { class rule1 {}; }
namespace tao_pegtl_namespace { REGISTER_RULE(your_namespace::rule1); }
// rule2.h
#include "registered_rules.h"
namespace your_namespace { class rule2 {}; }
namespace tao_pegtl_namespace { REGISTER_RULE(your_namespace::rule2); }
// rules_collection.h
namespace tao_pegtl_namespace {
TYPE_COLLECTOR_END(registered_rules);
struct rules_collection : templateClass<(unpack `registered_rules`...)> {}
}
这不允许您使用包含所有动作专门知识的其他文件(如您所说的 SpecificTemplate.h )。我不知道tao::pegtl
是什么,所以我可能完全不在这里。