【发布时间】:2021-01-07 00:40:23
【问题描述】:
目标
我尝试创建一组类来删除样板代码,以便在 C++ 中实现对游戏的扩展。
为此,我有一个指定的 value 类,它可以包含以下类型之一:
float、std::string、bool、std::vector<value>、void
为此,我想要一个 host 类,我可以向其中添加一个或多个 method 实例,如下所示:
using namespace std::string_literals;
host h;
h.add(
method<bool, req<std::string>, req<std::string>, opt<bool>>("compare_strings"s,
[](std::string s_orig, std::string s_comp, std::optional<bool> ingore_case) -> bool {
if (ignore_case.has_value() && ignore_case.value()) {
// ... lowercase both
}
return s_orig.compare(s_comp) == 0;
}));
请注意,req<T> 应该是需要给定值的元信息,opt<T> 应该是不需要给定值并且只能在所有必需参数之后提供的元信息。
host 类现在包含一个方法execute(std::string function, std::vector<value> values),其中function 和values 源自一个方法,该方法为方法获取char* 和'char** argv+ int argcfor values. Theexecute@987654339 @method` 实例函数
value host::execute(std::string function, std::vector<value> values) {
// get matching method group
std::vector<method> mthds = m_methods[function];
// get matching parameter list
for (method& mthd : mthds) {
if (mthd.can_call(mthds, values)) {
// call generic method
auto res = mthd.call_generic(values);
// pass result back to callee
// return [...]
}
}
// return error back to callee
// return [...]
}
这意味着实际的method 类现在需要正确处理can_call 和call_generic 两个方法。
value 类有对应的template<typename T> bool is() 和template<typename T> T get() 方法。
剩下的
我确实对此进行了其他尝试,但由于失败了,我删除了它们(在后面不是很聪明,但需要把整个事情弄出来,因为另一个人依赖于结果的工作)现在无法想出另一个尝试那么之前......所以这就是我现在所剩下的:
class method_base
{
public:
template<typename T> struct in { using type = T; };
template<typename T> struct opt { using type = T; };
public:
virtual bool can_call(std::vector<sqf::value> values) = 0;
virtual sqf::value call_generic(std::vector<sqf::value> values) = 0;
};
template<typename T, typename ... TArgs>
class method : public method_base
{
func m_func;
sqf::value val
public:
using func = T(*)(TArgs...);
method(func f) : m_func(f) {}
virtual retval can_call(std::vector<sqf::value> values) override
{
}
};
附录
如果有什么不清楚、令人困惑或您还有其他问题,请务必提出。我会尽力改写任何不清楚的地方,因为这将极大地帮助未来开发进一步的扩展,可能会定义一种“转到”方式,以便如何在社区中为相关游戏创建扩展(以防万一,Arma 3有人想知道)
我可能会注意到,这几乎是我第一次深入研究元编程,所以我介绍的东西可能根本不可能。如果是这样,我想问你是否也可以解释为什么会这样,我尝试的事情是不可能的。
解决方案
我想对所有再次回答这个问题的人表示感谢。我最终在这里结合了所有解决方案的几乎所有部分,并且在此过程中学到了很多东西。我最终得到的最终实现如下所示:
namespace meta
{
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
template <typename ArgType>
struct def_value { static ArgType value() { return {}; } };
template <typename ArgType>
struct get_type { using type = ArgType; };
template <typename ArgType>
struct get_type<std::optional<ArgType>> { using type = ArgType; };
}
struct method {
std::function<bool(const std::vector<value>&)> m_can_call;
std::function<value(const std::vector<value>&)> m_call;
template <typename ... Args, std::size_t... IndexSequence>
static bool can_call_impl(const std::vector<value>& values, std::index_sequence<IndexSequence...> s) {
// values max args
return values.size() <= sizeof...(Args) &&
// for every Arg, either...
(... && (
// the value provides that argument and its the correct type, or...
(IndexSequence < values.size() && sqf::is<sqf::meta::get_type<Args>::type>(values[IndexSequence])) ||
// the value does not provide that argument and the arg is an optional
(IndexSequence >= values.size() && sqf::meta::is_optional_v<Args>)
));
}
template <typename Ret, typename ... Args, std::size_t... IndexSequence>
static value call_impl(std::function<Ret(Args...)> f, const std::vector<value>& values, std::index_sequence<IndexSequence...>) {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(f,
(IndexSequence < values.size() ? sqf::get<sqf::meta::get_type<Args>::type>(values[IndexSequence])
: sqf::meta::def_value<Args>::value())...)
};
}
public:
template <typename Ret, typename ... Args>
method(std::function<Ret(Args...)> f) :
m_can_call([](const std::vector<value>& values) -> bool
{
return can_call_impl<Args...>(values, std::index_sequence_for<Args...>{});
}),
m_call([f](const std::vector<value>& values) -> value
{
return call_impl<Ret, Args...>(f, values, std::index_sequence_for<Args...>{});
})
{
}
bool can_call(const std::vector<value>& values) const { return m_can_call(values); }
value call_generic(const std::vector<value>& values) const { return m_call(values); }
// to handle lambda
template <typename F>
method static create(F f) { return method{ std::function{f} }; }
};
【问题讨论】:
标签: c++ templates variadic-templates template-meta-programming