Skip to content

Commit 72d2ded

Browse files
committed
add support for arbitrary nested namespaces
1 parent 99aa4b7 commit 72d2ded

File tree

5 files changed

+221
-37
lines changed

5 files changed

+221
-37
lines changed

source/context.cpp

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,48 @@ using ModuleGetter = std::function< pybind11::module & (std::string const &) >;
9393
9494
{1}
9595
96+
static std::vector<std::string> const reserved_python_words{{
97+
"nonlocal",
98+
"global",
99+
}};
100+
std::string mangle_namespace_name(std::string const &ns) {{
101+
if( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
102+
return ns + '_';
103+
}}
104+
105+
void makeSubmodules(const std::vector<std::string> &moduleNames, std::map <std::string, pybind11::module> &modules) {{
106+
std::string prevNamespace = "";
107+
std::string curNamespace = "";
108+
for (const auto &curMod : moduleNames) {{
109+
if (curNamespace.size() > 0)
110+
curNamespace += "::"; // don't add :: to the start
111+
const auto mangledMod = mangle_namespace_name(curMod);
112+
curNamespace += mangledMod;
113+
if (modules.count(curNamespace) == 0) {{
114+
modules[curNamespace] = modules[prevNamespace].def_submodule(mangledMod.c_str(), ("Bindings for " + curNamespace + " namespace").c_str());
115+
}}
116+
prevNamespace = curNamespace;
117+
}}
118+
}}
119+
120+
96121
PYBIND11_MODULE({2}, root_module) {{
97122
root_module.doc() = "{2} module";
98123
99124
std::map <std::string, pybind11::module> modules;
100125
ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {{
101-
auto it = modules.find(namespace_);
126+
auto it = modules.find(mangle_namespace_name(namespace_));
102127
if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!");
103128
return it->second;
104129
}};
105130
106131
modules[""] = root_module;
107132
108-
static std::vector<std::string> const reserved_python_words {{"nonlocal", "global", }};
109-
110-
auto mangle_namespace_name(
111-
[](std::string const &ns) -> std::string {{
112-
if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
113-
return ns+'_';
114-
}}
115-
);
116-
117-
std::vector< std::pair<std::string, std::string> > sub_modules {{
133+
std::vector< std::vector<std::string> > sub_modules {{
118134
{3} }};
119-
for(auto &p : sub_modules ) modules[ p.first.empty() ? p.second : p.first+"::"+p.second ] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
135+
for(auto &p : sub_modules ) {{
136+
makeSubmodules(p, modules);
137+
}}
120138
121139
//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
122140
@@ -213,22 +231,20 @@ void Context::add_to_binded(CXXRecordDecl const *C)
213231
/// examine binded objects and recursivly create all nested namespaces
214232
std::set<string> Context::create_all_nested_namespaces()
215233
{
216-
vector<string> namespaces;
234+
std::set<string> namespaces;
217235

218236
for( auto &b : binders ) {
219237
if( b->code().size() ) {
220238
string ns = namespace_from_named_decl(b->named_decl());
221239

222240
while( ns.size() ) {
223-
namespaces.push_back(ns);
241+
namespaces.insert(ns);
224242
ns = base_namespace(ns);
225243
}
226244
}
227245
}
228246

229-
std::set<string> s(namespaces.begin(), namespaces.end());
230-
231-
return s;
247+
return namespaces;
232248
}
233249

234250
std::string Context::module_variable_name(std::string const &namespace_)
@@ -454,9 +470,15 @@ void Context::generate(Config const &config)
454470
string namespace_pairs;
455471
std::set<string> namespaces = create_all_nested_namespaces();
456472
for( auto &n : namespaces ) {
457-
if( n.size() ) namespace_pairs += "\t\t{{\"{}\", \"{}\"}},\n"_format(base_namespace(n), last_namespace(n));
458-
modules += n;
459-
modules += ' ';
473+
if( n.size() ) {
474+
const auto curMods = split(n, "::");
475+
namespace_pairs += "\t\t{";
476+
for( size_t i = 0; i < curMods.size(); i++ ) {
477+
namespace_pairs += "\"" + curMods[i] + "\"";
478+
if( i != curMods.size() - 1 ) namespace_pairs += ", ";
479+
}
480+
namespace_pairs += "},\n";
481+
}
460482
}
461483
replace(modules, "::", ".");
462484

source/util.cpp

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,21 @@ namespace binder {
3636

3737

3838
/// Split string using given separator
39-
vector<string> split(string const &buffer, string const &separator)
40-
{
41-
string line;
42-
vector<string> lines;
43-
44-
for( uint i = 0; i < buffer.size(); ++i ) {
45-
if( buffer.compare(i, separator.size(), separator) ) line.push_back(buffer[i]);
46-
else {
47-
lines.push_back(line);
48-
line.resize(0);
49-
}
50-
}
51-
52-
if( line.size() ) lines.push_back(line);
53-
54-
return lines;
39+
std::vector<std::string> split(const std::string &s, const std::string &delimiter) {
40+
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
41+
std::string token;
42+
std::vector<std::string> res;
43+
44+
while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) {
45+
token = s.substr (pos_start, pos_end - pos_start);
46+
pos_start = pos_end + delim_len;
47+
res.push_back (token);
48+
}
49+
50+
res.push_back (s.substr (pos_start));
51+
return res;
5552
}
5653

57-
5854
/// Replace all occurrences of string
5955
void replace_reverse(string &r, string const &from, string const &to)
6056
{

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ set( binder_tests
141141
T42.stl.names.multiset
142142
T43.stl.pybind11_include_stl
143143
T50.namespace_binder
144+
T51.nested_namespace
144145
T60.custom_shared
145146
)
146147
if (pybind11_VERSION VERSION_LESS 2.5.99)

test/T51.nested_namespace.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
#ifndef _INCLUDED_T51_NESTED_NAMESPACES_
3+
#define _INCLUDED_T51_NESTED_NAMESPACES_
4+
5+
namespace aaaa::bbbb::cccc {
6+
void foo_aaaa_bbbb_cccc() {}
7+
namespace dddd{
8+
void bar_aaaa_bbbb_cccc_dddd() {}
9+
}
10+
namespace eeee {
11+
void baz_aaaa_bbbb_cccc_eeee() {}
12+
}
13+
}
14+
15+
#endif // _INCLUDED_T51_NESTED_NAMESPACES_

test/T51.nested_namespace.ref.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// File: T51_nested_namespace.cpp
2+
#include <T51.nested_namespace.hpp> // aaaa::bbbb::cccc::foo_aaaa_bbbb_cccc
3+
4+
#include <functional>
5+
#include <pybind11/pybind11.h>
6+
#include <string>
7+
8+
#ifndef BINDER_PYBIND11_TYPE_CASTER
9+
#define BINDER_PYBIND11_TYPE_CASTER
10+
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>, false)
11+
PYBIND11_DECLARE_HOLDER_TYPE(T, T*, false)
12+
PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
13+
#endif
14+
15+
void bind_T51_nested_namespace(std::function< pybind11::module &(std::string const &namespace_) > &M)
16+
{
17+
// aaaa::bbbb::cccc::foo_aaaa_bbbb_cccc() file:T51.nested_namespace.hpp line:6
18+
M("aaaa::bbbb::cccc").def("foo_aaaa_bbbb_cccc", (void (*)()) &aaaa::bbbb::cccc::foo_aaaa_bbbb_cccc, "C++: aaaa::bbbb::cccc::foo_aaaa_bbbb_cccc() --> void");
19+
20+
}
21+
22+
23+
// File: T51_nested_namespace_1.cpp
24+
#include <T51.nested_namespace.hpp> // aaaa::bbbb::cccc::dddd::bar_aaaa_bbbb_cccc_dddd
25+
26+
#include <functional>
27+
#include <pybind11/pybind11.h>
28+
#include <string>
29+
30+
#ifndef BINDER_PYBIND11_TYPE_CASTER
31+
#define BINDER_PYBIND11_TYPE_CASTER
32+
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>, false)
33+
PYBIND11_DECLARE_HOLDER_TYPE(T, T*, false)
34+
PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
35+
#endif
36+
37+
void bind_T51_nested_namespace_1(std::function< pybind11::module &(std::string const &namespace_) > &M)
38+
{
39+
// aaaa::bbbb::cccc::dddd::bar_aaaa_bbbb_cccc_dddd() file:T51.nested_namespace.hpp line:8
40+
M("aaaa::bbbb::cccc::dddd").def("bar_aaaa_bbbb_cccc_dddd", (void (*)()) &aaaa::bbbb::cccc::dddd::bar_aaaa_bbbb_cccc_dddd, "C++: aaaa::bbbb::cccc::dddd::bar_aaaa_bbbb_cccc_dddd() --> void");
41+
42+
}
43+
44+
45+
// File: T51_nested_namespace_2.cpp
46+
#include <T51.nested_namespace.hpp> // aaaa::bbbb::cccc::eeee::baz_aaaa_bbbb_cccc_eeee
47+
48+
#include <functional>
49+
#include <pybind11/pybind11.h>
50+
#include <string>
51+
52+
#ifndef BINDER_PYBIND11_TYPE_CASTER
53+
#define BINDER_PYBIND11_TYPE_CASTER
54+
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>, false)
55+
PYBIND11_DECLARE_HOLDER_TYPE(T, T*, false)
56+
PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
57+
#endif
58+
59+
void bind_T51_nested_namespace_2(std::function< pybind11::module &(std::string const &namespace_) > &M)
60+
{
61+
// aaaa::bbbb::cccc::eeee::baz_aaaa_bbbb_cccc_eeee() file:T51.nested_namespace.hpp line:11
62+
M("aaaa::bbbb::cccc::eeee").def("baz_aaaa_bbbb_cccc_eeee", (void (*)()) &aaaa::bbbb::cccc::eeee::baz_aaaa_bbbb_cccc_eeee, "C++: aaaa::bbbb::cccc::eeee::baz_aaaa_bbbb_cccc_eeee() --> void");
63+
64+
}
65+
66+
67+
#include <map>
68+
#include <algorithm>
69+
#include <functional>
70+
#include <memory>
71+
#include <stdexcept>
72+
#include <string>
73+
#include <iostream>
74+
75+
#include <pybind11/pybind11.h>
76+
77+
using ModuleGetter = std::function< pybind11::module & (std::string const &) >;
78+
79+
void bind_T51_nested_namespace(std::function< pybind11::module &(std::string const &namespace_) > &M);
80+
void bind_T51_nested_namespace_1(std::function< pybind11::module &(std::string const &namespace_) > &M);
81+
void bind_T51_nested_namespace_2(std::function< pybind11::module &(std::string const &namespace_) > &M);
82+
83+
84+
static std::vector<std::string> const reserved_python_words{
85+
"nonlocal",
86+
"global",
87+
};
88+
std::string mangle_namespace_name(std::string const &ns) {
89+
if( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
90+
return ns + '_';
91+
}
92+
93+
void makeSubmodules(const std::vector<std::string> &moduleNames, std::map <std::string, pybind11::module> &modules) {
94+
std::string prevNamespace = "";
95+
std::string curNamespace = "";
96+
for (const auto &curMod : moduleNames) {
97+
std::cout << "making: " << curMod << std::endl;
98+
if (curNamespace.size() > 0)
99+
curNamespace += "::"; // don't add :: to the start
100+
const auto mangledMod = mangle_namespace_name(curMod);
101+
std::cout << "mangled: " << mangledMod << std::endl;
102+
curNamespace += mangledMod;
103+
if (modules.count(curNamespace) == 0) {
104+
std::cout << "defining namespace " << curNamespace << " in " << prevNamespace << std::endl;
105+
modules[curNamespace] = modules[prevNamespace].def_submodule(mangledMod.c_str(), ("Bindings for " + curNamespace + " namespace").c_str());
106+
}
107+
prevNamespace = curNamespace;
108+
}
109+
}
110+
111+
112+
PYBIND11_MODULE(T51_nested_namespace, root_module) {
113+
root_module.doc() = "T51_nested_namespace module";
114+
115+
std::map <std::string, pybind11::module> modules;
116+
ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {
117+
auto it = modules.find(mangle_namespace_name(namespace_));
118+
if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!");
119+
return it->second;
120+
};
121+
122+
modules[""] = root_module;
123+
124+
std::vector< std::vector<std::string> > sub_modules {
125+
{"aaaa"},
126+
{"aaaa", "bbbb"},
127+
{"aaaa", "bbbb", "cccc"},
128+
{"aaaa", "bbbb", "cccc", "dddd"},
129+
{"aaaa", "bbbb", "cccc", "eeee"},
130+
};
131+
for(auto &p : sub_modules ) {
132+
makeSubmodules(p, modules);
133+
}
134+
135+
//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
136+
137+
bind_T51_nested_namespace(M);
138+
bind_T51_nested_namespace_1(M);
139+
bind_T51_nested_namespace_2(M);
140+
141+
}
142+
143+
// Source list file: /home/matt/workspace/binder/build/test//T51_nested_namespace.sources
144+
// T51_nested_namespace.cpp
145+
// T51_nested_namespace.cpp
146+
// T51_nested_namespace_1.cpp
147+
// T51_nested_namespace_2.cpp
148+
149+
// Modules list file: /home/matt/workspace/binder/build/test//T51_nested_namespace.modules
150+
//

0 commit comments

Comments
 (0)