Repository: RosettaCommons/binder (language: python)

Issue:
Forward declared inner functions
Hi,
I ran into a problem when binding classes with forward declared inner classes.
In particular, when binding the code
```
struct outer {
	struct inner { int a; };  // ok
	struct inner2;             // ok
	struct inner3;             // not ok

	void f( inner *i, inner2 *i2, inner3 *i3 ){};

	struct inner2 { int b; };
};

struct outer::inner3 { int c; };
```
the inner class `inner3` will not be included.
However, I would have expected bindings for `inner3` as well.
Do you agree?


Discussion / hints:

Fix patch:
diff --git a/source/class.cpp b/source/class.cpp
index c3d406d2..570306a9 100644
--- a/source/class.cpp
+++ b/source/class.cpp
@@ -1080,6 +1080,11 @@ string ClassBinder::bind_nested_classes(Context &context)
 {
 	string c;
 	for_public_nested_classes([&c, &context, this](CXXRecordDecl const *innerC) {
+		if ( !innerC->isFirstDecl() ) return; // avoid multiple bindings: try to bind innerC only at first declaration
+		if ( !innerC->isThisDeclarationADefinition() && innerC->hasDefinition() ) {
+			// innerC seems to be a forward declaration
+			innerC = innerC->getDefinition();
+		}
 		if(is_bindable(innerC)) {
 			//c += "\t// Binding " + C->getNameAsString() + ";\n";
 			ClassBinder b(innerC);


Test patch:
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index a825585b..3e5b90d4 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -91,6 +91,7 @@ set( binder_tests
          T12.operator
          T15.copy
          T15.inner_class
+         T15.inner_class.fwd
          T17.anonymous
          T20.template
          T30.include
diff --git a/test/T15.inner_class.fwd.hpp b/test/T15.inner_class.fwd.hpp
new file mode 100644
index 00000000..63084aa7
--- /dev/null
+++ b/test/T15.inner_class.fwd.hpp
@@ -0,0 +1,31 @@
+// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
+// vi: set ts=2 noet:
+
+#ifndef T15_INNER_CLASS_FWD_H
+#define T15_INNER_CLASS_FWD_H
+
+/// @file   binder/test/T15.inner_class.fwd.hpp
+/// @brief  Binder self-test file. Tests for bindings of forward declared inner functions.
+
+struct outer {
+
+	struct inner {
+		int a;
+	};
+
+	struct inner2;
+
+	struct inner3;
+
+	void f( inner *i, inner2 *i2, inner3 *i3 ){};
+
+	struct inner2 {
+		int b;
+	};
+};
+
+struct outer::inner3 {
+	int c;
+};
+
+#endif
\ No newline at end of file
diff --git a/test/T15.inner_class.fwd.ref b/test/T15.inner_class.fwd.ref
new file mode 100644
index 00000000..19ebec64
--- /dev/null
+++ b/test/T15.inner_class.fwd.ref
@@ -0,0 +1,90 @@
+// File: T15_inner_class_fwd.cpp
+#include <T15.inner_class.fwd.hpp> // outer
+#include <T15.inner_class.fwd.hpp> // outer::inner
+#include <T15.inner_class.fwd.hpp> // outer::inner2
+#include <sstream> // __str__
+
+#include <pybind11/pybind11.h>
+#include <functional>
+#include <string>
+
+#ifndef BINDER_PYBIND11_TYPE_CASTER
+	#define BINDER_PYBIND11_TYPE_CASTER
+	PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
+	PYBIND11_DECLARE_HOLDER_TYPE(T, T*)
+	PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
+#endif
+
+void bind_T15_inner_class_fwd(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // outer file:T15.inner_class.fwd.hpp line:11
+		pybind11::class_<outer, std::shared_ptr<outer>> cl(M(""), "outer", "binder/test/T15.inner_class.fwd.hpp\n \n\n  Binder self-test file. Tests for bindings of forward declared inner functions.\n \n\n Thomas Feierl");
+		cl.def( pybind11::init( [](){ return new outer(); } ) );
+		cl.def("f", (void (outer::*)(struct outer::inner *, struct outer::inner2 *, struct outer::inner3 *)) &outer::f, "C++: outer::f(struct outer::inner *, struct outer::inner2 *, struct outer::inner3 *) --> void", pybind11::arg("i"), pybind11::arg("i2"), pybind11::arg("i3"));
+
+		{ // outer::inner file:T15.inner_class.fwd.hpp line:13
+			auto & enclosing_class = cl;
+			pybind11::class_<outer::inner, std::shared_ptr<outer::inner>> cl(enclosing_class, "inner", "");
+			cl.def( pybind11::init( [](){ return new outer::inner(); } ) );
+			cl.def_readwrite("a", &outer::inner::a);
+		}
+
+		{ // outer::inner2 file:T15.inner_class.fwd.hpp line:23
+			auto & enclosing_class = cl;
+			pybind11::class_<outer::inner2, std::shared_ptr<outer::inner2>> cl(enclosing_class, "inner2", "");
+			cl.def( pybind11::init( [](){ return new outer::inner2(); } ) );
+			cl.def_readwrite("b", &outer::inner2::b);
+		}
+
+		{ // outer::inner3 file:T15.inner_class.fwd.hpp line:28
+			auto & enclosing_class = cl;
+			pybind11::class_<outer::inner3, std::shared_ptr<outer::inner3>> cl(enclosing_class, "inner3", "");
+			cl.def( pybind11::init( [](){ return new outer::inner3(); } ) );
+			cl.def_readwrite("c", &outer::inner3::c);
+		}
+
+	}
+}
+
+
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <functional>
+#include <string>
+
+#include <pybind11/pybind11.h>
+
+typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
+
+void bind_T15_inner_class_fwd(std::function< pybind11::module &(std::string const &namespace_) > &M);
+
+
+PYBIND11_MODULE(T15_inner_class_fwd, root_module) {
+	root_module.doc() = "T15_inner_class_fwd module";
+
+	std::map <std::string, pybind11::module> modules;
+	ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {
+		auto it = modules.find(namespace_);
+		if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!");
+		return it->second;
+	};
+
+	modules[""] = root_module;
+
+	std::vector< std::pair<std::string, std::string> > sub_modules {
+	};
+	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule(p.second.c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
+
+	//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
+
+	bind_T15_inner_class_fwd(M);
+
+}
+
+// Source list file: /home/thomas/Projects/thfe/binder/build/test//T15_inner_class_fwd.sources
+// T15_inner_class_fwd.cpp
+// T15_inner_class_fwd.cpp
+
+// Modules list file: /home/thomas/Projects/thfe/binder/build/test//T15_inner_class_fwd.modules
+//