how to use template "inheritance" to avoid boilerplate code when binding C++ to python with pybind11 - pybind11

I managed to bind template definitions doing
py::class_<T<a>>(m, "Ta")
py::class_<T<b>>(m, "Tb")
...
where T is the template class and a and b typenames defining the template. But the problem I have tells me that this is not the really good way to expose these template definitions, because now I am forced to duplicate code for all the functions and parameters of the template class T:
py::class_<T<a>>(m, "Ta")
.def(py::init<string, string>())
.def("do_smthg", &T<a>::do_sthg)
...;
py::class_<T<b>>(m, "Tb")
.def(py::init<string, string>())
.def("do_smthg", &T<b>::do_sthg)
...;
when class T itself for instance has do_smthg, that I would then expect to have to define only once. I tried to browse the doc and the web, but I failed to find the right pointer.

You may just create a simple helper function that instantiates a list of templates, like following:
template <typename x, typename ... Tail, typename ModT>
void ApplyTemplate(ModT & m, std::vector<std::string> names) {
py::class_<T<x>>(m, names.at(0))
.def(py::init<string, string>())
.def("do_smthg", &T<x>::do_sthg)
;
names.erase(names.begin());
if constexpr(sizeof...(Tail) > 0)
ApplyTemplate<Tail...>(m, names);
}
Now call this function within module context with all possible arguments to template:
PYBIND11_MODULE(example, m) {
ApplyTemplate<a, b>(m, {"Ta", "Tb"});
}
If for some reason your T template is not available within ApplyTemplate context then you may pass it as a template <typename> typename argument:
template <template <typename> typename T, typename x, typename ... Tail, typename ModT>
void ApplyTemplate(.....
Instead of function you can also use a simple macro:
PYBIND11_MODULE(example, m) {
// Define macro
#define MY_CLASS(param, name) \
py::class_<T<param>>(m, name) \
.def(py::init<string, string>()) \
.def("do_smthg", &T<param>::do_sthg) \
;
// Use macro
MY_CLASS(a, "Ta");
MY_CLASS(b, "Tb");
// Remove macro
#undef MY_CLASS
}

Related

PyBind11 Template Class of Many Types

I'd like to use PyBind11 to wrap a specialized array class. However, the array is available in many flavours (one per each plain-old-datatype). The code looks like this:
py::class_<Array2D<float>>(m, "Array2Dfloat", py::buffer_protocol(), py::dynamic_attr())
.def(py::init<>())
.def(py::init<Array2D<float>::xy_t,Array2D<float>::xy_t,float>())
.def("size", &Array2D<float>::size)
.def("width", &Array2D<float>::width)
.def("height", &Array2D<float>::height)
//...
//...
The only way I've thought of to tell PyBind11 about these classes is by duplicating the above for each POD through the use of a very large macro.
Is there a better way to do this?
You can avoid using macros and instead go with a templated declaration function:
template<typename T>
void declare_array(py::module &m, std::string &typestr) {
using Class = Array2D<T>;
std::string pyclass_name = std::string("Array2D") + typestr;
py::class_<Class>(m, pyclass_name.c_str(), py::buffer_protocol(), py::dynamic_attr())
.def(py::init<>())
.def(py::init<Class::xy_t, Class::xy_t, T>())
.def("size", &Class::size)
.def("width", &Class::width)
.def("height", &Class::height);
}
And then call it multiple times:
declare_array<float>(m, "float");
declare_array<int>(m, "int");
...

Why does C++11 fail to treat two template typenames T == U in constructor in a template class?

I could not find a short and better title. :(
Suppose I have a simple C++11 template class definition as below:
#include <utility>
template <typename T>
class A
{
public:
T v;
A(){};
template <typename U>
A(const A<U>& a); // copy ctor
A(A<T>&& a); // move ctor
};
template <typename T>
template <typename U>
A<T>::A(const A<U>& a) // copy ctor
{
v = a.v;
}
template <typename T> // move ctor
A<T>::A(A<T>&& a)
{
v = std::move(a.v); // although moving PODs does not make sense in my example
}
Now, my C++11 code uses the above C++11 class as follows:
int main()
{
A<char> a;
A<float> b(a); // okay
A<char> c(a); // gcc output is as below:
// error: use of deleted function 'constexpr A<char>::A(const A<char>&)'
// note: 'constexpr A<char>::A(const A<char>&)' is implicitly declared
// as deleted because 'A<char>' declares a move constructor or move
// assignment operator
return 0;
}
It gives the error use of deleted function 'constexpr A<char>::A(const A<char>&)'.
However, it compiles and runs properly when I am not using any move semantics in the class definition as below:
#include <utility>
template <typename T>
class A
{
public:
T v;
A(){};
template <typename U>
A(const A<U>& a);
// A(A<T>&& a); // removed move ctor
};
template <typename T>
template <typename U>
A<T>::A(const A<U>& a)
{
v = a.v;
}
My questions are:
Why the gcc compiler is treating the template <typename U> in copy ctor differently in the two situations?
Why does it fail to treat typenames T == U in the presence of move ctor?
Why do I need to explicitly write yet another template
function template <typename T> A<T>::A(const A<U>& a) when using
move ctor?
You haven't written a copy constructor. From [class.copy] in the C++11 standard:
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).
(I see no provision for a template constructor to be called a copy constructor)
and consequently
If the class definition does not explicitly declare a copy constructor, one is declared implicitly
The difference between the two examples is whether or not you have a move constructor:
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4)
Defining the move constructor disabled the copy constructor. What you want is a converting constructor, which as the name implies, converts its argument to the type of the class. If your copy/move constructors don't do anything special, either omit or default them. To explain the final piece of your confusion, the reason you can omit the template arguments in your fake copy constructor is because of the injected class name. Meaning that wherever you see A, it's silently substituted for A<T>. I've included it for clarity.
template <typename T>
class A
{
public:
T v;
A() = default;
template <typename U>
A<T>(const A<U>& a);
A(const A<T>& a) = default;
A(A<T>&& a) = default;
};

Variadic Template And Ellipses

I have two function defined as follows
void Function( ... );
template < typename ... ARGS >
void Function1( ARGS ... args )
{
// I have to call Function here
}
How can i pass variadic template arguments "args" to Function.
You should be able to just call Function(args...).
The ... operator in C++11 expands template parameter packs which is exactly what you want here. See here for more details.

Using boost::program_options with own template class possible?

I'm currently start using boost::program_options for parsing command line options as well as configuration files.
Is it possible to use own template classes as option arguments? That means, something like
#include <iostream>
#include "boost/program_options.hpp"
namespace po = boost::program_options;
template <typename T>
class MyClass
{
private:
T* m_data;
size_t m_size;
public:
MyClass( size_t size) : m_size(size) { m_data = new T[size]; }
~MyClass() { delete[] m_data; }
T get( size_t i ) { return m_data[i]; }
void set( size_t i, T value ) { m_data[i] = value; }
};
int main (int argc, const char * argv[])
{
po::options_description generic("General options");
generic.add_options() ("myclass", po::value< MyClass<int>(2) >(),
"Read MyClass");
return 0;
}
Trying to compile this I get an Semantic Issue (No matching function for call to 'value'). I guess I need to provide some casting to an generalized type but I have no real idea.
Can anybody help?
Thanks
Aeon512
I wouldn't know if boost::program_options allows the use-case you are trying, but the error you are getting is because your are trying to pass an object as a template type to po::value<>. If the size is known at compile-time, you could have the size be passed in as a template parameter.
template< typename T, size_t size >
class MyClass {
T m_data[size];
public:
// ...
};
And then use it like so:
po::value< MyClass<int, 2> >()
You should also look into using Boost.Array instead that I guess fulfills what you are trying to implement.
I would write it like this:
MyClass<int> mine(2);
generic.add_options() ("myclass", po::value(&mine), "Read MyClass");
Then all that needs to be done is to define an input stream operator like this:
std::istream& operator >>(std::istream& source, MyClass& target);
Then Boost Program Options will invoke this stream operator when the myclass option is used, and your object will be automatically populated according to that operator's implementation, rather than having to later call one of the Program Options functions to extract the value.
If you don't prefer the above syntax, something like should work too:
generic.add_options() ("myclass", po::value<MyClass<int> >()->default_value(MyClass<int>(2)), "Read MyClass");
This way you would be creating the instance of your class directly with your desired constructor argument outside of the template part where runtime behavior isn't allowed. I do not prefer this way because it's verbose and you end up needing to call more functions later to convert the value.

Why isn't my static member function recognised across assemblies?

I have a helper assembly which includes a function to identify object types:
namespace Util
{
using namespace System;
public ref class CastingHelpers
{
public:
template < class T, class U >
static System::Boolean isinst(U u);
static bool Test() {return true;}
};
}
...but for some reason, when I try and use it in a gui application which references the assembly:
Util::CastingHelpers::Test();
Util::CastingHelpers::isinst<SomeClass^>(someInstance);
..gives me an error:
2>.\DataProcessor.cpp(161) : error C2039: 'isinst' : is not a member of 'Util::CastingHelpers'
Note that test works fine. Is this something to do with the fact that isinst uses generics?
You are not creating a generic function, you are creating a C++ template function which is not exported from the assembly.
Use the keyword generic instead of template to create .NET generic types and methods.
The template method is only visible by code that #includes its declaration.