如何使用pybind11自动绑定可变参数类模板的模板化成员函数?

时间:2020-10-27 10:52:32

标签: python c++ templates variadic-templates pybind11

我想编写一组便利函数,以帮助我使用pybind11绑定类模板实现。我希望这些功能尽可能通用/可重用。

具体地说,我想到的是班级模板

  • 取决于可变参数模板template <class... Ts>
  • 具有几个依赖于一个模板参数T的成员函数,该模板参数预计来自参数包Ts...

我的目标是编写模板化函数

template <class... Ts>
bind_MyClass(){...}

为给定的模板参数绑定类模板MyClass,并为Ts...中的每种类型自动绑定其模板化的成员函数。

作为一个最小的示例,请考虑以下std::tuple的包装器:

template <class... Ts>
class Tuple
{
public:
    template <typename T>
    T& get()
    {
        return std::get<T>(_tuple);
    }

    template <typename T>
    void set(T const& val)
    {
        this->get<T>() = val;
    }

private:
    std::tuple<Ts...> _tuple{};
};

借助some help from stackoverflow,我弄清楚了如何使用以下代码绑定Tuple::get的所有实现:

namespace py = pybind11;

/********************************
 * customized pretty type names *
 ********************************/

template <typename T>
std::string pretty_name()
{
    std::ostringstream o;
    o << typeid(T).name();
    return o.str();
}

template <> std::string pretty_name<int>        (){ return "int";      }
template <> std::string pretty_name<std::string>(){ return "str";      }
template <> std::string pretty_name<double>     (){ return "double";   }
template <> std::string pretty_name<Tuple<int>> (){ return "TupleInt"; }
template <> std::string pretty_name<Tuple<double, std::string>> (){ return "TupleDoubleStr"; }

/**********************************************************************
 * Bind the "get" template of Tuple for all types in a parameter pack *
 **********************************************************************/

template <class C>
void bind_member_template_for_all_T(py::class_<C>&, std::string const&){}

template <class C, class T, class... Ts>
void bind_member_template_for_all_T(py::class_<C>& c, std::string const& basename)
{
    // bind for T
    c.def((basename + pretty_name<T>()).c_str(), &C::template get<T>);

    // recursively bind for each T in Ts...
    bind_member_template_for_all_T<C, Ts...>(c, basename);
}

/**************************************************
 * Bind the Tuple class for a given pack of types *
 **************************************************/

template <class... Ts>
void bindTuple(py::module& m, std::string const& name)
{
    using Class = Tuple<Ts...>;
    py::class_<Class> c(m, name.c_str());
    c.def(py::init<>());

    bind_member_template_for_all_T<Class, Ts...>(c, "get_");

}

/*********************************************************
 * Actually bind some realizations of the Tuple template *
 *********************************************************/

PYBIND11_MODULE(cmake_example, m)
{
    bindTuple<int>(m, "TupleInt");
    bindTuple<double, std::string>(m, "TupleDoubleString");
    bindTuple<Tuple<int>, Tuple<double, std::string>>(m, "WeirdNestedTuple");
}

是否可以以某种方式概括bind_member_template_for_all_T,以便我可以向其抛出其他成员模板,例如Tuple::set?还是我必须为要绑定的每个成员编写两个bind_member_template_for_all_T重载?后者会使我的代码杂乱无章。

您可以在此Github repo中找到此示例的源代码。

编辑:

当然,一种选择是为每个类而不是每个类成员编写一个bind_member_template_for_all_T函数:

template <class C>
void bind_member_template_for_all_T(py::class_<C>&){}

template <class C, class T, class... Ts>
void bind_member_template_for_all_T(py::class_<C>& c)
{
    c.def(("get_" + pretty_name<T>()).c_str(), &C::template get<T>)
     .def(("set_" + pretty_name<T>()).c_str(), &C::template set<T>);

    bind_member_template_for_all_T<C, Ts...>(c);
}

template <class... Ts>
void bindTuple(py::module& m, std::string const& name)
{
    using Class = Tuple<Ts...>;
    py::class_<Class> c(m, name.c_str());
    c.def(py::init<>());

    //bind set, set const and get
    bind_member_template_for_all_T<Class, Ts...>(c);

}

但是,如果可以将依赖于可变参数模板的任何类的功能归纳为一个函数,那仍然很好。

0 个答案:

没有答案