c++ - Collect template parameters at compile time
问题描述
I have a class which takes many other classes as template arguments (for compile-time grammar generation with TAO PEGTL if it matters) and would like to know if there is a better and more extensible way to do this, than typing everything manually.
Current Situation:
//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
}
Currently this has to be done manually (and in multiple locations for different "collection" classes).
Is there a way to change this into a better manageable alternative like:
Desired Situation:
//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()
I was trying to achieve this with boost PP and MPL for the last days but I'm not sure if it is possible at all.
EDIT:
Specific instances are required by pegtl like this: There are actions which are predefined as:
template<typename Rule>
struct Action : tao::pegtl::nothing<Rule> {
};
And have to be instantiated as:
template<>
struct Action<specificRule> {
static void apply0(State &state) {
state.rule = specificRule::ID;
}
};
解决方案
规则登记
我建议在代码库的一个位置显式手动注册所有“规则”(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{};
在“super_rule.hpp”中定义- 附加到“registered_rules.hpp”中
SuperRule
的列表。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
.
C++17 中的完整示例
#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]
推荐阅读
- python - 错误:只能比较标记相同的 DataFrame 对象
- python - 如何修复 python 3.7 中的“RuntimeError: input(): lost sys.stdin”错误
- python - 使用布尔键值对将字符串转换为字典格式
- linux - Shell 命令显示不同的行为.. 在终端与运行 scipt
- exception - 如何通过中间件从控制器(或服务)捕获异常?
- javascript - 我正在尝试运行 gulp serve 并说“未找到 gulpfile”。我是 Nodejs 的新手。我不知道我的吞咽路径是否错误
- r - 按值识别 json 键并提取所有匹配项
- python - 如何为按面积(1/面积)定义的 voronoi 多边形着色?
- python - 从 http 请求流式传输二进制音频数据以进行 librosa 分析
- python - 从 HTML/Flask 运行 Python 函数