3

I'm trying to understand the static cast as used in the Pybind11 docs here. Specifically, they use the syntax

static_cast<void (Pet::*)(int)>(&Pet::set)

Since I haven't seen this syntax before I'm strugling to interpret and apply to my own code so I was hoping somebody could explain what's going on here. Thanks

Edit - some context

I am creating Pybind11 bindings to an overloaded method that has two signatures, which only differ by const qualification. The class I am binding is a template so I am using this strategy to create the bindings

    template<class T>
    class Matrix {
    public:

        ...

        /**
         * get the row names
         */
        std::vector<std::string> &getRowNames() {
            return rowNames;
        }

        /**
         * get the row names (mutable)
         */
        const std::vector<std::string> &getRowNames() {
            return rowNames;
        }

    ...

My version of the helper function described in that post is this:

template<typename T>
void declare_matrix(py::module &m, const std::string &typestr) {
    using Class = ls::Matrix<T>;
    const std::string &pyclass_name = typestr;
    py::class_<Class>(m, pyclass_name.c_str(), py::buffer_protocol(), py::dynamic_attr())
            .def(py::init<unsigned int, unsigned int>())
            .def("getRowNames", static_cast<const std::vector<std::string>(ls::Matrix<T>::*)()>(&ls::Matrix<T>::getRowNames))

but the getRowNames line produces the following error:

Address of overloaded function 'getRowNames' cannot be static_cast to type 'const std::vector<std::string> (ls::Matrix<complex<double>>::*)()'

For anybody else reading this, the cast I was able to figure out thanks to the answer is:

static_cast< std::vector<std::string>& (ls::Matrix<T>::*)()>(&Class::getRowNames)
CiaranWelsh
  • 7,014
  • 10
  • 53
  • 106
  • 1
    `set` is a method of the `Pet` class, and the cast is converting (for some reason) a pointer to that to a pointer to a method of the `Pet` class which takes one `int` parameter and has a `void` return. – john Nov 01 '20 at 13:29
  • Looking at the documentation it says `We can disambiguate by casting them to function pointers.`. If you don't need to do that disambiguation in your code then you don't need to use this technique. – john Nov 01 '20 at 13:31
  • I do need the disambiguation unfortunately, my two overloaded functions only differ by `const` (see edits) – CiaranWelsh Nov 01 '20 at 13:37
  • Are you having some specific problem (if so what) or are you just trying to understand? I must admit I've not seen this particular technique before. – john Nov 01 '20 at 13:40
  • You are missing a `&` in your cast. Try this `static_cast&(ls::Matrix::*)()>` – john Nov 01 '20 at 13:47
  • @john Both actually, I've updated the question with my specific problem but it'd also be nice to understand it – CiaranWelsh Nov 01 '20 at 13:48
  • 1
    Well I've pointed out the problem (I hope). The technique relies on the fact that the cast selects only the method that matches the type of the cast and discards the others. Just using the name alone would be ambiguous. – john Nov 01 '20 at 13:51

1 Answers1

3

The meaning of:

static_cast<void (Pet::*)(int)>(&Pet::set)
  • static_cast<T_1>(T_2) means we're casting type 2 to type 1.
  • T_1:
    • (Pet::*) is a pointer to a class member of Pet (see https://stackoverflow.com/a/9939367/14344821 for a further discussion)
    • void (Pet::*)(int) is a pointer to a member function that takes an int parameter, returning a void
  • T_2
    • &Pet::set is the memory location of Pet::set

So basically, we're stating explicitly that we are setting the integer value.

Now we can bind the set functions to python (allowing us to set both age and name):

   .def("set", static_cast<void (Pet::*)(int)>(&Pet::set), "Set the pet's age")
   .def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name");
Casper Dijkstra
  • 1,615
  • 10
  • 37