Repository: RosettaCommons/binder (language: python)

Issue:
[question] customization of a trampoline function definition
Binder generates automatically some trampoline classes when needed and generates their corresponding member functions.

I am currently facing a particular case where I need to modify one of the generated member function of one of the trampoline classes (I will provide more details bellow for the interested developers/users). It seems that there is currently no ways to do it using binder configuration options. @lyskov do you have recommendation on how to do so?

----------------------
More detailed context:

> Given a virtual function meant to return new instances of a type via a pointer to a base class, and a derived type declared in python, the return value cast won't keep the python object alive, resulting in a failure when attempting to call the virtual method on the new instance via the pointer. Because this function is called from c++, the return value policy has no effect.

https://github.com/pybind/pybind11/issues/1774#issue-437889724
https://github.com/pybind/pybind11/issues/1049#issue-254369984

Work around that requires modification of the binder generated trampoline member function:

https://github.com/pybind/pybind11/issues/1049#issuecomment-326688270

Other potential work around on the pybind11 side that might solves the issues without binder modifications:
https://github.com/pybind/pybind11/blob/smart_holder/README_smart_holder.rst I did not find an ETA for the merging of this branch into master.
https://github.com/pybind/pybind11/pull/2839 this PR has no activities since a year ago

In the case that I faced, the solution of https://github.com/pybind/pybind11/issues/1049#issuecomment-326688270 worked.
To do so, I generated the trampoline class using binder and then I modified the clone member function to be consistent with https://github.com/pybind/pybind11/issues/1049#issuecomment-326688270 with the shared pointer class that I am using. I could write a script that post-process the binder generated files and replace the definition of the function with one that suites my need in this case but this is not the cleanest way to resolve this and I am curious to have your point of view @lyskov on this.
Add module_local option for namespace to the configuration script
This PR addresses #248 by adding an option to enable `module_local` for a namespace (using `+module_local namespace`) or for all the namespaces(using `+module_local ALL`).

@lyskov 


Discussion / hints:
@kliegeois If i have to face this then i would keep option writing post-processing-scripts at the bottom of my option list. While it certainly easy to do it is also most likely will be a highest maintenance option in the long run. I think it should be really straightforward way to add this functionality to the Binder and that would be my recommendation if you are do not mind writing a bit of C++ code (i do not think anything fancy will be needed here). 

The way i see it we can add per-member-function Binder config option that will instruct Binder to alter trampoline function generation. Let me know if you open to this idea and would like to implement it and i will outline changes thats need to be done (should be ~one-afternoon task unless i am overlooking something big). Thanks,
@lyskov I totally agree with your point of view and your proposition. I can probably work on this by the Christmas break. You can list here or send me an email with any recommendations for this. Thanks!
I have given this more thoughts and it i see a two ways we can implement this. Neither is perfect so some extra discussion might be needed:

[a] Simplest approach would probably be to add add config option that will allow user to request python-counterpart creation for pure-virtual-functions. Code to modify for this will be around this line: https://github.com/RosettaCommons/binder/blob/master/source/class.cpp#L740. I imagine the logic would be about the following: if member function we about to bind is pure-virtual (ie `m->isPure()`) then check if user requested special handling for this function (say if config option `+trampoline_create_python_counter_part MyClass::my_function` was specified) and generate code outlined in https://github.com/pybind/pybind11/issues/1049#issuecomment-326688270. Main issue with this approach is that it is not clear how to generalize this for function with type signature different from `clone` (ie `shared_ptr<MyClass> clone()`). (i am not sure about option name so `trampoline_create_python_counter_part` name is just a place holder for now)

[b] another approach would be to allow user to fully customize how particular trampoline function is generated similar to options `+binder` and `+add_on_binder` that we have for classes, say `+trampoline_member_function_binder` (tentative name). User supplied `trampoline_binder` will probably need to be templated with class type, function return type etc. And have type signature identical to member function that we about to bind but take a few extra arguments (class name as string, function name as string, ...). Issue with this approach is that user might need to provide multiple custom function for different classes and also need to be very careful when implementing it (ie be familiar with https://github.com/RosettaCommons/binder/blob/master/source/class.cpp#L663). So this whole approach while quite general seems a bit of overkill to me.

At the moment I am leaning more towards [a] since it is much easier both to write and use and see how it goes. Thoughts?

Thanks @lyskov for your comment!

I was thinking about something similar to [b], mostly because [a] does not provide enough customization. For example, if you use a custom holder type, https://github.com/pybind/pybind11/issues/1049#issuecomment-326688270 is not exactly what you need: you need to update the code snippet to be consistent with your used holder type and its corresponding functions/constructor.

I was thinking of something as follows:

Given files: 
`include_1.hpp`:

```
#include <pybind11/pybind11.h>
namespace py = pybind11;

class A {
public:
    A() = default;
    A(const A&) {}
    virtual ~A() = default;

    virtual void func() = 0;
    virtual std::shared_ptr<A> clone() const = 0;
};
```
`include_2.hpp`:
```
template <typename T, typename T_trampoline>
std::shared_ptr<T> myCloneFunction(const T *ptr_to_this) {
        auto self = py::cast(ptr_to_this);
        auto cloned = self.attr("clone")();

        auto keep_python_state_alive = std::make_shared<py::object>(cloned);
        auto ptr = cloned.cast<T_trampoline*>();

        // aliasing shared_ptr: points to `T_trampoline* ptr` but refcounts the Python object
        return std::shared_ptr<T>(keep_python_state_alive, ptr);
    }
```

`Config_file.cfg`:
```
+include_for_trampoline_class A <include_2.hpp>
+trampoline_member_function_binder A::clone myCloneFunction
```

Then, binder would generate something similar to this:

```
#include <include_2.hpp>

class A_trampoline: public A {
public:
    using A::A;
    A_trampoline(const A& a) : A(a) {}

    void func() override { PYBIND11_OVERLOAD_PURE(void, A, func,); }

    std::shared_ptr<A> clone() const override {
        return myCloneFunction<A, A_trampoline>(this);
    }
};
```

This would allow the user to customize `myCloneFunction`. What do you think of this? Do you think that it could work and would be clean?
I agree that approach [a] seems way to rigid. Ok, lets see if we can make [b] work. A few thoughts on proposed design:

> `+include_for_trampoline_class A <include_2.hpp>`

-- no need to custom include directive, we can re-use include_for_class here:  `+include_for_class <include_2.hpp>`

Generated code:
1. It would be best to allow user to fully rewrite arbitrary trampoline function generation. In order to make this work i think we will have to pass name of the class and name of the function as strings (see Binder generated code here: https://github.com/RosettaCommons/binder/blob/master/test/T10.virtual_inheritance.ref#L27) (I am also considering to pass pointer to parent class member function that we are overriding, it is currently not needed so maybe no for now. But i am curious to hear your thoughts on this). So expected type signature of trampoline function should `original_return_type f(string const &, string const &, **original args)`
2. I think we also need to declare function specified by `trampoline_member_function_binder` as class friend (otherwise it will not be able to access protected members)

Thanks @lyskov I will update the PR as soon as possible based on your comments.
@lyskov, I know it is not ideal on your side but could we merge the PR as is? It is easier on my side as I don't need to ask for a review. I can add those comments in the next PR (if it is fine with you).

Fix patch:
diff --git a/documentation/config.rst b/documentation/config.rst
index f402ded9..7c9305f1 100644
--- a/documentation/config.rst
+++ b/documentation/config.rst
@@ -202,14 +202,26 @@ Config file directives:
 
 * ``default_call_guard``, optionally specify a call guard applied to all function definitions. See `pybind11 documentation <https://pybind11.readthedocs.io/en/stable/advanced/functions.html#call-guard>`_. Default None.
 
+.. code-block:: bash
+
+  +default_member_pointer_return_value_policy           pybind11::return_value_policy::reference
+  +default_member_lvalue_reference_return_value_policy  pybind11::return_value_policy::reference_internal
+  +default_member_rvalue_reference_return_value_policy  pybind11::return_value_policy::move
+  +default_call_guard pybind11::gil_scoped_release
 
 * ``+custom_shared``: specify a custom shared pointer class that Binder should use instead of std::shared_ptr.
 
+* ``module_local_namespace``: use to add (or remove) the extra argument module_local to the pybind11 classes and enum of a namespace. This option can be used for all the namaspaces of a given project using `+module_local_namespace @all_namespaces`.
+
+.. code-block:: bash
+
+  +module_local_namespace @all_namespaces
+  -module_local_namespace std
 
+* ``trampoline_member_function_binder``: use to specify a custom trampoline member function defined by the user in a given header file.
 
 .. code-block:: bash
 
-  +default_member_pointer_return_value_policy           pybind11::return_value_policy::reference
-  +default_member_lvalue_reference_return_value_policy  pybind11::return_value_policy::reference_internal
-  +default_member_rvalue_reference_return_value_policy  pybind11::return_value_policy::move
-  +default_call_guard pybind11::gil_scoped_release
+  +include_for_class aaa::A <T81.custom_trampoline_with_args.include>
+  +trampoline_member_function_binder aaa::A::foo myFoo
+
diff --git a/source/class.cpp b/source/class.cpp
index 5a9fe267..c79391f0 100644
--- a/source/class.cpp
+++ b/source/class.cpp
@@ -737,9 +737,22 @@ string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const
 
 				c += "\t{} {}({}){} {}override {{"_format(return_type, m->getNameAsString(), std::get<0>(args), m->isConst() ? " const" : "", exception_specification);
 
-				c += indent(fmt::format(call_back_function_body_template, class_name, /*class_qualified_name(C), */ python_name, std::get<1>(args), return_type), "\t\t");
-				if( m->isPure() ) c += "\t\tpybind11::pybind11_fail(\"Tried to call pure virtual function \\\"{}::{}\\\"\");\n"_format(C->getNameAsString(), python_name);
-				else c += "\t\treturn {}::{}({});\n"_format(C->getNameAsString(), m->getNameAsString(), std::get<1>(args));
+				string member_function_name = namespace_from_named_decl(C);
+				if ( member_function_name.length() > 0 ) member_function_name += "::";
+				member_function_name +=  C->getNameAsString() + "::" + m->getNameAsString();
+				string custom_function_info = Config::get().is_custom_trampoline_function_requested(member_function_name);
+				if( custom_function_info == "" ) {
+					c += indent(fmt::format(call_back_function_body_template, class_name, /*class_qualified_name(C), */ python_name, std::get<1>(args), return_type), "\t\t");
+					if( m->isPure() ) c += "\t\tpybind11::pybind11_fail(\"Tried to call pure virtual function \\\"{}::{}\\\"\");\n"_format(C->getNameAsString(), python_name);
+					else c += "\t\treturn {}::{}({});\n"_format(C->getNameAsString(), m->getNameAsString(), std::get<1>(args));
+				}
+				else {
+					string input_args = std::get<1>(args);
+					c += "\n\t\treturn {}<{},{}>(this, \"{}\", \"{}\""_format(custom_function_info, C->getNameAsString(), callback_structure_name(C), class_name, m->getNameAsString());
+					if ( input_args.length() > 0 )
+						c += ", {}"_format(std::get<1>(args));
+					c += ");\n";
+				}
 				c += "\t}\n";
 
 				// c += string(m->isPure() ? "PYBIND11_OVERLOAD_PURE_NAME" : "PYBIND11_OVERLOAD_NAME") + '(';
diff --git a/source/config.cpp b/source/config.cpp
index a372c966..022564d8 100644
--- a/source/config.cpp
+++ b/source/config.cpp
@@ -83,6 +83,8 @@ void Config::read(string const &file_name)
 	string const _default_member_lvalue_reference_return_value_policy_{"default_member_lvalue_reference_return_value_policy"};
 	string const _default_member_rvalue_reference_return_value_policy_{"default_member_rvalue_reference_return_value_policy"};
 
+	string const _trampoline_member_function_binder_{"trampoline_member_function_binder"};
+
 	string const _default_call_guard_{"default_call_guard"};
 
 	std::ifstream f(file_name);
@@ -209,6 +211,13 @@ void Config::read(string const &file_name)
 		else if( token == _default_member_rvalue_reference_return_value_policy_ ) default_member_rvalue_reference_return_value_policy_ = name_without_spaces;
 		else if( token == _default_call_guard_ ) default_call_guard_ = name_without_spaces;
 
+		else if( token == _trampoline_member_function_binder_ ) {
+			if( bind ) {
+				auto member_function_name_and_function_name = split_in_two(name, "Invalid line for trampoline_member_function_binder specification! Must be: qualified_class_name::member_funtion_name + <space or tab> + name_of_function. Got: " + line);
+				custom_trampoline_function_[member_function_name_and_function_name.first] = member_function_name_and_function_name.second;
+			}
+		}
+
 		else {
 			throw std::runtime_error("Invalid token in config file! Each token must be either: namespace, class or function! For example: '+function aaa::bb::my_function'. Token: '" + token +
 									 "' Line: '" + line + '\'');
@@ -344,6 +353,15 @@ bool Config::is_class_skipping_requested(string const &class__) const
 	return false;
 }
 
+
+string Config::is_custom_trampoline_function_requested(string const &function_) const
+{
+	auto it = custom_trampoline_function_.find(function_);
+	if( it == custom_trampoline_function_.end() ) return "";
+	else return it->second;
+}
+
+
 bool Config::is_buffer_protocol_requested(string const &class__) const
 {
 	string class_{class__};
diff --git a/source/config.hpp b/source/config.hpp
index 7a3bc260..8adfb904 100644
--- a/source/config.hpp
+++ b/source/config.hpp
@@ -39,7 +39,7 @@ class Config
 
 private:
 	std::map<string, string> binders_, add_on_binders_;
-	std::map<string, string> binder_for_namespaces_, add_on_binder_for_namespaces_;
+	std::map<string, string> binder_for_namespaces_, add_on_binder_for_namespaces_, custom_trampoline_function_;
 
 	std::map<string, std::vector<string> > class_includes_, namespace_includes_;
 
@@ -104,6 +104,8 @@ class Config
 
 	bool is_include_skipping_requested(string const &include) const;
 
+	string is_custom_trampoline_function_requested(string const &function__) const;
+
 	string includes_code() const;
 };
 


Test patch:
diff --git a/test/T70.options.hpp b/test/T70.options.hpp
index 1670a27b..68b4a75e 100644
--- a/test/T70.options.hpp
+++ b/test/T70.options.hpp
@@ -13,6 +13,7 @@ namespace aaaa {
 struct AAAA {};
 void foo_aaaa() {}
 
+enum E1 { E1_V0, E1_V1 };
 
 namespace cccc {
 
diff --git a/test/T70.options.ref b/test/T70.options.ref
index 491290e4..f9d98e4c 100644
--- a/test/T70.options.ref
+++ b/test/T70.options.ref
@@ -1,5 +1,6 @@
 // File: T70_options.cpp
 #include <T70.options.hpp> // aaaa::AAAA
+#include <T70.options.hpp> // aaaa::E1
 #include <T70.options.hpp> // aaaa::foo_aaaa
 #include <sstream> // __str__
 
@@ -23,6 +24,14 @@ void bind_T70_options(std::function< pybind11::module &(std::string const &names
 	// aaaa::foo_aaaa() file:T70.options.hpp line:14
 	M("aaaa").def("foo_aaaa", (void (*)()) &aaaa::foo_aaaa, "C++: aaaa::foo_aaaa() --> void");
 
+	// aaaa::E1 file:T70.options.hpp line:16
+	pybind11::enum_<aaaa::E1>(M("aaaa"), "E1", pybind11::arithmetic(), "")
+		.value("E1_V0", aaaa::E1_V0)
+		.value("E1_V1", aaaa::E1_V1)
+		.export_values();
+
+;
+
 }
 
 
@@ -44,44 +53,16 @@ void bind_T70_options(std::function< pybind11::module &(std::string const &names
 
 void bind_T70_options_1(std::function< pybind11::module &(std::string const &namespace_) > &M)
 {
-	{ // aaaa::cccc::CCCC file:T70.options.hpp line:19
+	{ // aaaa::cccc::CCCC file:T70.options.hpp line:20
 		pybind11::class_<aaaa::cccc::CCCC, std::shared_ptr<aaaa::cccc::CCCC>> cl(M("aaaa::cccc"), "CCCC", "");
 		cl.def( pybind11::init( [](){ return new aaaa::cccc::CCCC(); } ) );
 	}
-	// aaaa::cccc::foo_CCCC() file:T70.options.hpp line:20
+	// aaaa::cccc::foo_CCCC() file:T70.options.hpp line:21
 	M("aaaa::cccc").def("foo_CCCC", (void (*)()) &aaaa::cccc::foo_CCCC, "C++: aaaa::cccc::foo_CCCC() --> void");
 
 }
 
 
-// File: T70_options_2.cpp
-#include <T70.options.hpp> // aaaa::bbbb::BBBB
-#include <T70.options.hpp> // aaaa::bbbb::foo_bbbb
-#include <sstream> // __str__
-
-#include <functional>
-#include <pybind11/pybind11.h>
-#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_T70_options_2(std::function< pybind11::module &(std::string const &namespace_) > &M)
-{
-	{ // aaaa::bbbb::BBBB file:T70.options.hpp line:27
-		pybind11::class_<aaaa::bbbb::BBBB, std::shared_ptr<aaaa::bbbb::BBBB>> cl(M("aaaa::bbbb"), "BBBB", "");
-		cl.def( pybind11::init( [](){ return new aaaa::bbbb::BBBB(); } ) );
-	}
-	// aaaa::bbbb::foo_bbbb() file:T70.options.hpp line:28
-	M("aaaa::bbbb").def("foo_bbbb", (void (*)()) &aaaa::bbbb::foo_bbbb, "C++: aaaa::bbbb::foo_bbbb() --> void");
-
-}
-
-
 #include <map>
 #include <algorithm>
 #include <functional>
@@ -95,7 +76,6 @@ typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
 
 void bind_T70_options(std::function< pybind11::module &(std::string const &namespace_) > &M);
 void bind_T70_options_1(std::function< pybind11::module &(std::string const &namespace_) > &M);
-void bind_T70_options_2(std::function< pybind11::module &(std::string const &namespace_) > &M);
 
 
 PYBIND11_MODULE(T70_options, root_module) {
@@ -121,7 +101,6 @@ PYBIND11_MODULE(T70_options, root_module) {
 
 	std::vector< std::pair<std::string, std::string> > sub_modules {
 		{"", "aaaa"},
-		{"aaaa", "bbbb"},
 		{"aaaa", "cccc"},
 	};
 	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
@@ -130,7 +109,6 @@ PYBIND11_MODULE(T70_options, root_module) {
 
 	bind_T70_options(M);
 	bind_T70_options_1(M);
-	bind_T70_options_2(M);
 
 }
 
@@ -138,7 +116,6 @@ PYBIND11_MODULE(T70_options, root_module) {
 // T70_options.cpp
 // T70_options.cpp
 // T70_options_1.cpp
-// T70_options_2.cpp
 
 // Modules list file: TEST/T70_options.modules
-// aaaa aaaa.bbbb aaaa.cccc 
+// aaaa aaaa.cccc 
diff --git a/test/T71.module_local.hpp b/test/T71.module_local.hpp
index 27f88862..83dd6b3f 100644
--- a/test/T71.module_local.hpp
+++ b/test/T71.module_local.hpp
@@ -18,6 +18,8 @@ namespace aaa {
 		public:
 			void foo(){};
 	};
+
+	enum E1 { E1_V0, E1_V1 };
 }
 
 namespace bbb {
diff --git a/test/T71.module_local.ref b/test/T71.module_local.ref
index 5beccc1b..1f7a5d92 100644
--- a/test/T71.module_local.ref
+++ b/test/T71.module_local.ref
@@ -1,5 +1,6 @@
 // File: T71_module_local.cpp
 #include <T71.module_local.hpp> // aaa::A
+#include <T71.module_local.hpp> // aaa::E1
 #include <sstream> // __str__
 
 #include <functional>
@@ -20,6 +21,14 @@ void bind_T71_module_local(std::function< pybind11::module &(std::string const &
 		cl.def( pybind11::init( [](){ return new aaa::A(); } ) );
 		cl.def("foo", (void (aaa::A::*)()) &aaa::A::foo, "C++: aaa::A::foo() --> void");
 	}
+	// aaa::E1 file:T71.module_local.hpp line:22
+	pybind11::enum_<aaa::E1>(M("aaa"), "E1", pybind11::arithmetic(), "", pybind11::module_local())
+		.value("E1_V0", aaa::E1_V0)
+		.value("E1_V1", aaa::E1_V1)
+		.export_values();
+
+;
+
 }
 
 
@@ -40,7 +49,7 @@ void bind_T71_module_local(std::function< pybind11::module &(std::string const &
 
 void bind_T71_module_local_1(std::function< pybind11::module &(std::string const &namespace_) > &M)
 {
-	{ // bbb::B file:T71.module_local.hpp line:24
+	{ // bbb::B file:T71.module_local.hpp line:26
 		pybind11::class_<bbb::B, std::shared_ptr<bbb::B>> cl(M("bbb"), "B", "");
 		cl.def( pybind11::init( [](){ return new bbb::B(); } ) );
 		cl.def("foo", (void (bbb::B::*)()) &bbb::B::foo, "C++: bbb::B::foo() --> void");
diff --git a/test/T80.custom_trampoline.config b/test/T80.custom_trampoline.config
new file mode 100644
index 00000000..6e5dbf9f
--- /dev/null
+++ b/test/T80.custom_trampoline.config
@@ -0,0 +1,2 @@
++include_for_class aaa::A <T80.custom_trampoline.include>
++trampoline_member_function_binder aaa::A::foo myFoo
\ No newline at end of file
diff --git a/test/T80.custom_trampoline.hpp b/test/T80.custom_trampoline.hpp
new file mode 100644
index 00000000..686df133
--- /dev/null
+++ b/test/T80.custom_trampoline.hpp
@@ -0,0 +1,28 @@
+// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
+// vi: set ts=2 noet:
+//
+// Copyright (c) 2016 Sergey Lyskov <sergey.lyskov@jhu.edu>
+//
+// All rights reserved. Use of this source code is governed by a
+// MIT license that can be found in the LICENSE file.
+
+/// @file   binder/test/T71.module_local.hpp
+/// @author Sergey Lyskov
+
+#ifndef _INCLUDED_T80_custom_trampoline_hpp_
+#define _INCLUDED_T80_custom_trampoline_hpp_
+
+namespace aaa {
+	template <typename T>
+	class A 
+	{
+		public:
+			virtual A<T> foo(){
+				return A<T>();
+			};
+	};
+}
+
+template class aaa::A<double>;
+
+#endif // _INCLUDED_T80_custom_trampoline_hpp_
diff --git a/test/T80.custom_trampoline.include b/test/T80.custom_trampoline.include
new file mode 100644
index 00000000..3e4b8989
--- /dev/null
+++ b/test/T80.custom_trampoline.include
@@ -0,0 +1,6 @@
+#include<string>
+
+template <typename A, typename trampoline_A>
+A myFoo(const trampoline_A *ptr_to_this, const std::string &class_name, const std::string &function_name) { 
+    return A();
+};
\ No newline at end of file
diff --git a/test/T80.custom_trampoline.ref b/test/T80.custom_trampoline.ref
new file mode 100644
index 00000000..7c9e24be
--- /dev/null
+++ b/test/T80.custom_trampoline.ref
@@ -0,0 +1,90 @@
+// File: T80_custom_trampoline.cpp
+#include <T80.custom_trampoline.hpp> // aaa::A
+#include <T80.custom_trampoline.include> // +include_for_class
+#include <sstream> // __str__
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#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
+
+// aaa::A file:T80.custom_trampoline.hpp line:26
+struct PyCallBack_aaa_A_double_t : public aaa::A<double> {
+	using aaa::A<double>::A;
+
+	class aaa::A<double> foo() override {
+		return myFoo<A,PyCallBack_aaa_A_double_t>(this, "aaa::A<double>", "foo");
+	}
+};
+
+void bind_T80_custom_trampoline(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // aaa::A file:T80.custom_trampoline.hpp line:26
+		pybind11::class_<aaa::A<double>, std::shared_ptr<aaa::A<double>>, PyCallBack_aaa_A_double_t> cl(M("aaa"), "A_double_t", "");
+		cl.def( pybind11::init( [](){ return new aaa::A<double>(); }, [](){ return new PyCallBack_aaa_A_double_t(); } ) );
+		cl.def( pybind11::init( [](PyCallBack_aaa_A_double_t const &o){ return new PyCallBack_aaa_A_double_t(o); } ) );
+		cl.def( pybind11::init( [](aaa::A<double> const &o){ return new aaa::A<double>(o); } ) );
+		cl.def("foo", (class aaa::A<double> (aaa::A<double>::*)()) &aaa::A<double>::foo, "C++: aaa::A<double>::foo() --> class aaa::A<double>");
+		cl.def("assign", (class aaa::A<double> & (aaa::A<double>::*)(const class aaa::A<double> &)) &aaa::A<double>::operator=, "C++: aaa::A<double>::operator=(const class aaa::A<double> &) --> class aaa::A<double> &", pybind11::return_value_policy::automatic, pybind11::arg(""));
+	}
+}
+
+
+#include <map>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <pybind11/pybind11.h>
+
+typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
+
+void bind_T80_custom_trampoline(std::function< pybind11::module &(std::string const &namespace_) > &M);
+
+
+PYBIND11_MODULE(T80_custom_trampoline, root_module) {
+	root_module.doc() = "T80_custom_trampoline 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;
+
+	static std::vector<std::string> const reserved_python_words {"nonlocal", "global", };
+
+	auto mangle_namespace_name(
+		[](std::string const &ns) -> std::string {
+			if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
+			else return ns+'_';
+		}
+	);
+
+	std::vector< std::pair<std::string, std::string> > sub_modules {
+		{"", "aaa"},
+	};
+	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
+
+	//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
+
+	bind_T80_custom_trampoline(M);
+
+}
+
+// Source list file: TEST/T80_custom_trampoline.sources
+// T80_custom_trampoline.cpp
+// T80_custom_trampoline.cpp
+
+// Modules list file: TEST/T80_custom_trampoline.modules
+// aaa 
diff --git a/test/T81.custom_trampoline_with_args.config b/test/T81.custom_trampoline_with_args.config
new file mode 100644
index 00000000..645188ed
--- /dev/null
+++ b/test/T81.custom_trampoline_with_args.config
@@ -0,0 +1,2 @@
++include_for_class aaa::A <T81.custom_trampoline_with_args.include>
++trampoline_member_function_binder aaa::A::foo myFoo
\ No newline at end of file
diff --git a/test/T81.custom_trampoline_with_args.hpp b/test/T81.custom_trampoline_with_args.hpp
new file mode 100644
index 00000000..b526bebc
--- /dev/null
+++ b/test/T81.custom_trampoline_with_args.hpp
@@ -0,0 +1,28 @@
+// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
+// vi: set ts=2 noet:
+//
+// Copyright (c) 2016 Sergey Lyskov <sergey.lyskov@jhu.edu>
+//
+// All rights reserved. Use of this source code is governed by a
+// MIT license that can be found in the LICENSE file.
+
+/// @file   binder/test/T71.module_local.hpp
+/// @author Sergey Lyskov
+
+#ifndef _INCLUDED_T81_custom_trampoline_with_args_hpp_
+#define _INCLUDED_T81_custom_trampoline_with_args_hpp_
+
+#include<string>
+
+namespace aaa {
+	template <typename T>
+	class A 
+	{
+		public:
+			virtual void foo(int a, std::string b, float d){};
+	};
+}
+
+template class aaa::A<double>;
+
+#endif // _INCLUDED_T81_custom_trampoline_with_args_hpp_
diff --git a/test/T81.custom_trampoline_with_args.include b/test/T81.custom_trampoline_with_args.include
new file mode 100644
index 00000000..dc8905f3
--- /dev/null
+++ b/test/T81.custom_trampoline_with_args.include
@@ -0,0 +1,4 @@
+#include<string>
+
+template <typename A, typename trampoline_A>
+void myFoo(const trampoline_A *ptr_to_this, const std::string &class_name, const std::string &function_name, int a, std::string b, float c) { };
\ No newline at end of file
diff --git a/test/T81.custom_trampoline_with_args.ref b/test/T81.custom_trampoline_with_args.ref
new file mode 100644
index 00000000..df1a3ea6
--- /dev/null
+++ b/test/T81.custom_trampoline_with_args.ref
@@ -0,0 +1,92 @@
+// File: T81_custom_trampoline_with_args.cpp
+#include <T81.custom_trampoline_with_args.hpp> // aaa::A
+#include <T81.custom_trampoline_with_args.include> // +include_for_class
+#include <iterator> // __gnu_cxx::__normal_iterator
+#include <memory> // std::allocator
+#include <sstream> // __str__
+#include <string> // std::basic_string
+#include <string> // std::char_traits
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#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
+
+// aaa::A file:T81.custom_trampoline_with_args.hpp line:26
+struct PyCallBack_aaa_A_double_t : public aaa::A<double> {
+	using aaa::A<double>::A;
+
+	void foo(int a0, std::string a1, float a2) override {
+		return myFoo<A,PyCallBack_aaa_A_double_t>(this, "aaa::A<double>", "foo", a0, a1, a2);
+	}
+};
+
+void bind_T81_custom_trampoline_with_args(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // aaa::A file:T81.custom_trampoline_with_args.hpp line:26
+		pybind11::class_<aaa::A<double>, std::shared_ptr<aaa::A<double>>, PyCallBack_aaa_A_double_t> cl(M("aaa"), "A_double_t", "");
+		cl.def( pybind11::init( [](){ return new aaa::A<double>(); }, [](){ return new PyCallBack_aaa_A_double_t(); } ) );
+		cl.def("foo", (void (aaa::A<double>::*)(int, std::string, float)) &aaa::A<double>::foo, "C++: aaa::A<double>::foo(int, std::string, float) --> void", pybind11::arg("a"), pybind11::arg("b"), pybind11::arg("d"));
+		cl.def("assign", (class aaa::A<double> & (aaa::A<double>::*)(const class aaa::A<double> &)) &aaa::A<double>::operator=, "C++: aaa::A<double>::operator=(const class aaa::A<double> &) --> class aaa::A<double> &", pybind11::return_value_policy::automatic, pybind11::arg(""));
+	}
+}
+
+
+#include <map>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <pybind11/pybind11.h>
+
+typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
+
+void bind_T81_custom_trampoline_with_args(std::function< pybind11::module &(std::string const &namespace_) > &M);
+
+
+PYBIND11_MODULE(T81_custom_trampoline_with_args, root_module) {
+	root_module.doc() = "T81_custom_trampoline_with_args 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;
+
+	static std::vector<std::string> const reserved_python_words {"nonlocal", "global", };
+
+	auto mangle_namespace_name(
+		[](std::string const &ns) -> std::string {
+			if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
+			else return ns+'_';
+		}
+	);
+
+	std::vector< std::pair<std::string, std::string> > sub_modules {
+		{"", "aaa"},
+	};
+	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
+
+	//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
+
+	bind_T81_custom_trampoline_with_args(M);
+
+}
+
+// Source list file: TEST/T81_custom_trampoline_with_args.sources
+// T81_custom_trampoline_with_args.cpp
+// T81_custom_trampoline_with_args.cpp
+
+// Modules list file: TEST/T81_custom_trampoline_with_args.modules
+// aaa