6

Using boost python I need create nested namespace.

Assume I have following cpp class structure:

namespace a
{
    class A{...}
    namespace b
    {
         class B{...}
    }
}

Obvious solution not work:

BOOST_PYTHON_MODULE( a ) {
    boost::python::class_<a::A>("A")
     ...
    ;
    BOOST_PYTHON_MODULE(b){
        boost::python::class_<a::b::B>("B")
        ...
    ;
    }
}

It causes compile-time error: linkage specification must be at global scope

Is there any way to declare class B that would be accessed from Python as a.b.B?

2
  • 1
    I put this in a comment because I have never tried it: isolation-nation.blogspot.com/2008/09/… Commented Oct 14, 2011 at 14:56
  • @Matthew - thanks, anyway usage of PyImport_AddModule is good way, that is why +1 Commented Oct 14, 2011 at 18:56

2 Answers 2

12

What you want is a boost::python::scope.

Python has no concept of 'namespaces', but you can use a class very much like a namespace:

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/scope.hpp>
using namespace boost::python;

namespace a
{
    class A{};

    namespace b
    {
         class B{};
    }
}

class DummyA{};
class DummyB{};

BOOST_PYTHON_MODULE(mymodule)
{
    // Change the current scope 
    scope a
        = class_<DummyA>("a")
        ;

    // Define a class A in the current scope, a
    class_<a::A>("A")
        //.def("somemethod", &a::A::method)
        ;

    // Change the scope again, a.b:
    scope b
        = class_<DummyB>("b")
        ;

    class_<a::b::B>("B")
        //.def("somemethod", &a::b::B::method)
        ;
}

Then in python, you have:

#!/usr/bin/env python
import mylib

print mylib.a,
print mylib.a.A
print mylib.a.b
print mylib.a.b.B

All a, a.A, a.b and a.b.B are actually classes, but you can treat a and a.b just like namespaces - and never actually instantiate them

Sign up to request clarification or add additional context in comments.

15 Comments

I was pleasantly surprised when I tried this - I wasn't actually expecting nesting scopes to work! Boost python is really very neat sometimes.
What if I want mylib.a, mylib.a.A, mylib.b, and mylib.b.B? How do I "exit" the "a" scope?
@robert boost::python::scope objects use RAII, so add extra {} to change the lifetime of the scope objects; when the scope object is destroyed it restores the namespace that was present when it was created.
@Autopulated: Your construction is different from that in the docs. In the docs, the scope object is created on assignment with an arbitrary name. Then the name used to create the subclass wrapper is the name of the enclosing class. What you do is create a namespace and then create a scope object with the same name, and then use that namespace/scope object name when defining the wrapper for the subclass, i.e. class_<a::A>. Can you explain the rationale for that?
@FaheemMitha: Because I didn't know that constructor existed! (And I don't see it in the documentation?) Using classes mirrors the way it works in python, so it's the only way I ever tried.
|
10

The trick with dummy classes is quite fine, but doesn't allow:

import mylib.a
from mylib.a.b import B

So, instead, use PyImport_AddModule(). You may find full featured examples in the following article: Packages in Python extension modules, by Vadim Macagon.

In short:

namespace py = boost::python;
std::string nested_name = py::extract<std::string>(py::scope().attr("__name__") + ".nested");
py::object nested_module(py::handle<>(py::borrowed(PyImport_AddModule(nested_name.c_str()))));
py::scope().attr("nested") = nested_module;
py::scope parent = nested_module;
py::class_<a::A>("A")...

1 Comment

the point that the above answer by James doesn't allow import mylib.a is quite important. reader, take note!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.