附加"政策"到函数参数

时间:2016-06-23 21:45:11

标签: c++ templates c++11 binding implicit-conversion

在我写的一些代码中,我有一堆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堆栈中读取相应的参数,并用参数调用目标函数if它成功了,如果没有,则发出lua错误信号。

这部分是有效的,有些工作,这个问题不是关于如何做到这一点。 (请不要告诉我有关luabindluabridge或其他现有库的问题,因为我无法解决这些问题并不适合我的项目。)

相反,我现在遇到的问题是,有时我希望输入参数的语义略有不同。

例如,有时参数应该是可选的。我将boost::optional的模板专门用于处理这种情况。因此,我可以在函数签名中使用boost::optional标记可选参数,并且包装器将知道如果缺少该参数,则它不是错误,它应该只传递boost::none。例如:

int my_function(lua_State * L, boost::optional<std::string>, std::string message);

因此boost::optional模板的使用类似于&#34;策略&#34;对于这里的输入,我基本上喜欢它是如何工作的。

这是一个我不太确定的问题:处理bool。在lua中,有一个正确的boolean类型,但是,lua也有contextually boolean的概念,类似于C ++的contextually convertible to bool概念。在lua中,值falsenil是假的,而其他所有值都是真实的。

通常情况下,如果你的c ++函数需要bool,那么用户可以期望他们可以传递任何值,并且你的界面会尊重truthiness,即使它是bool严格来说不是布尔值。但是,在其他情况下,您可能真的希望将其严格解释为true,并且如果他们不通过falseint my_function(lua_State * L, strict<bool> b, std::string message); 则会将其解释为用户错误

我希望能够做的是标记&#34;严格&#34;函数声明中的策略,看起来像

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>

允许从b.value进行一系列ref-qualified隐式转换,以引用该值。

这有多不安全?我没有看到这方面的重大安全漏洞,虽然我一直坚持&#34;隐含的转换是邪恶的&#34;口头禅。我在测试代码中玩了一些它似乎没有产生歧义或问题,但可能有一种聪明的方法可以让它做一些非常糟糕的事情,我没有想到。

如果这不是一个好主意,是否有比在任何地方键入int[] countOccurences = new int[1000]更好的策略,或者采用某种不同方式来操纵不会侵入类型的参数策略?< / p>

1 个答案:

答案 0 :(得分:1)

这样的事情应该这样做。

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