将"policy"附加到函数参数
Attaching a "policy" to a function parameter
在我正在编写的一些代码中,我有一堆c++函数,我试图以通用的方式绑定到lua。(然而,这个问题实际上与lua无关,它实际上是一个c++设计问题。)
这个想法是我可能有一个带有签名 的c++函数int my_function(lua_State * L, std::string server, std::string message);
例如,我希望能够将其推送到lua并将其暴露给用户脚本。
而lua只能直接接收签名int (lua_State *)
的函数。所以,我有一些模板,采用一个函数指针与上面的签名,并产生一个签名int(lua_State *)
的函数,它试图从lua堆栈中读取相应的参数,如果成功,则调用目标函数与参数,如果不成功,则向用户发出lua错误信号。
这部分是工作的,做了一些工作,这个问题不是关于如何做到这一点。(请不要告诉我关于luabind
, luabridge
或其他现有的库,因为我不能进入那些不适合我的项目。)
相反,我现在遇到的问题是,有时我希望输入参数具有稍微不同的语义。
例如,有时参数应该是可选的。为了处理这种情况,我专门为boost::optional
设计了模板。因此,我可以在函数签名中使用boost::optional
标记可选参数,并且包装器将知道如果该参数丢失,则不是错误,并且它应该传递boost::none
。例子:
int my_function(lua_State * L, boost::optional<std::string>, std::string message);
所以boost::optional
模板在这里被用作输入的"策略",我基本上喜欢它的工作方式。
这里有一个问题我不太确定:处理bool
。在lua中,有一个合适的boolean
类型,然而,lua也有一个contextually boolean
的概念,类似于c++的contextually convertible to bool
的概念。在lua中,false
和nil
为假值,其他值为真值。
通常,当你有一个接受bool
的c++函数时,用户会期望他们可以传递给它任何值,并且你的接口会尊重truthiness
,即使它不是严格意义上的布尔值。但是,在其他情况下,您可能真的希望将其严格解释为bool
,并且如果没有传递true
或false
,则将其解释为用户错误。
我想做的是在函数声明中标记"严格"策略,所以它看起来像
int my_function(lua_State * L, strict<bool> b, std::string message);
其中,strict
为模板,如
template <typename T>
struct strict {
T value;
};
,这个模板只有在我的包装器中才有意义。
令人讨厌的是,你必须在任何地方输入b.value
。
我想这样做:
template <typename T>
struct strict {
T value;
operator T & () & { return this->value; }
operator const T & () const & { return this->value; }
operator T && () && { return std::move(this->value); }
};
允许从strict<T>
到对值的引用的一系列ref限定的隐式转换。
这有多不安全?虽然我一直坚持"隐性转换是邪恶的"这一信条,但我并不认为这其中存在重大的安全漏洞。我在测试代码中稍微摆弄了一下,它似乎没有产生歧义或问题,但可能有一种聪明的方法可以使它做一些我没有想到的非常糟糕的事情。
如果这不是一个好主意,有没有比在任何地方输入b.value
更好的策略,或者有一些不同的方法来组合参数策略,而不会干扰类型?
应该是这样的。
visit
的重载是做这项工作的。注意optional
版本的递归调用。
#include <iostream>
#include <string>
#include <utility>
#include <iomanip>
#include <boost/optional.hpp>
// some boilerplate
template <typename T, template <typename, typename...> class Tmpl> // #1 see note
struct is_derived_from_template
{
typedef char yes[1];
typedef char no[2];
static no & test(...);
template <typename ...U>
static yes & test(Tmpl<U...> const &);
static bool constexpr value = sizeof(test(std::declval<T>())) == sizeof(yes);
};
template<typename T, template <typename, typename...> class Tmpl>
static constexpr bool is_derived_from_template_v = is_derived_from_template<T, Tmpl>::value;
// i dont know much about a lua_state but I guess it's a bit like this...
struct lua_state {
void set_string(std::size_t index, const std::string& s) {
std::cout << "index " << index << " setting string " << std::quoted(s) << std::endl;
}
void set_missing(std::size_t index) {
std::cout << "index " << index << " setting missing" << std::endl;
}
void set_int(std::size_t index, int i) {
std::cout << "index " << index << " setting int " << i << std::endl;
}
};
// policies
template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, std::string>::value>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
pstate->set_string(index, std::forward<T>(value));
}
template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, int>::value>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
pstate->set_int(index, std::forward<T>(value));
}
// special policy for optional
template<class T,
std::enable_if_t<is_derived_from_template_v<std::decay_t<T>, boost::optional>>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
if (value)
{
visit(index, pstate, std::forward<T>(value).value());
}
else {
pstate->set_missing(index);
}
}
// helper function
template<std::size_t...Is, class Tuple>
void set_vars_impl(lua_state* pstate, std::index_sequence<Is...>, Tuple&& tuple)
{
using expand = int [];
void(expand{ 0,
((visit(Is, pstate, std::get<Is>(std::forward<Tuple>(tuple)))),0)...
});
}
template<class...Ts>
void set_vars(lua_state* pstate, Ts&&...ts)
{
set_vars_impl(pstate,
std::make_index_sequence<sizeof...(Ts)>(),
std::make_tuple(std::forward<Ts>(ts)...));
}
int main(int argc, const char * argv[]) {
lua_state ls;
boost::optional<std::string> a { };
boost::optional<std::string> b { std::string { "hello" }};
std::string c = "world";
int d = 0;
boost::optional<int> e;
boost::optional<int> f { 1 };
set_vars(std::addressof(ls), a, b, c, d, e, f);
return 0;
}
预期结果:
index 0 setting missing
index 1 setting string "hello"
index 2 setting string "world"
index 3 setting int 0
index 4 setting missing
index 5 setting int 1
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- 当从函数参数中的临时值调用复制构造函数时
- 如何从"decltype()"获取函数参数的数量<funtion>?
- 如何将lambda作为模板类的成员函数参数
- 模板参数推导失败,函数参数/参数不匹配
- 如何在C++中将迭代器作为函数参数传递
- 将函数参数"const char*"转换为"std::string_view"是
- C++ 如何将数组值解压缩为函数参数
- 主函数参数的属性
- 具有两个间接寻址运算符 (C++) 的函数参数的用途
- "Warning: Comma within array index expression"但逗号分隔函数参数
- 如何定义在用作函数参数时工作的类模板的转换
- 将函数参数完美转发到函数指针:按值传递呢?
- 为什么我不能将引用作为 std::async 的函数参数传递
- 什么..(省略号)作为函数原型中唯一的函数参数,C++?
- 是否可以就地构造一个固定大小的数组作为函数参数?
- 接受模板作为函数参数
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- Arduino 函数参数