规则注册
我建议在代码库的一个位置显式手动注册所有“规则”(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 的新规则需要两个步骤:
- 在“super_rule.hpp”中定义
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 的原语。
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]