pybind11 repr 最近在使用 pybind11 做 C++ 接口导出 Python 绑定的工作,发现一个比较诡异的问题,当使用 pybind11/stl_bind.h 对 std::vector
或 std::map
这两种容器进行绑定时,如果容器的成员类型是 std::string
、int
等类型,自定义的 __repr__
接口总是无法生效,查看 __repr__
接口可以看到 __repr__
有重载实现:
1 2 3 4 5 6 7 8 9 | __repr__(...) | __repr__(*args, **kwargs) | Overloaded function. | | 1. __repr__(self: x.MapString2String) -> str | | Return the canonical string representation of this map . | | 2. __repr__(self: x.MapString2String) -> str
通过进一步阅读代码,发现是 pybind11/stl_bind.h 自作主张,当容器成员支持 operator<<
操作时,会自动插入 __repr__
实现(如下代码以 bind_map
为例,bind_vector
类似):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 template <typename , typename , typename ... Args> void map_if_insertion_operator (const Args &...) { }template <typename Map, typename Class_> auto map_if_insertion_operator (Class_ &cl, std::string const &name) -> decltype (std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void ()) { cl.def ("__repr__" , [name](Map &m) { std::ostringstream s; s << name << '{' ; bool f = false ; for (auto const &kv : m) { if (f) s << ", " ; s << kv.first << ": " << kv.second; f = true ; } s << '}' ; return s.str (); }, "Return the canonical string representation of this map." ); } template <typename Map, typename holder_type = std::unique_ptr<Map>, typename ... Args>class_<Map, holder_type> bind_map (handle scope, const std::string &name, Args&&... args) { ... detail::map_if_insertion_operator <Map, Class_>(cl, name); ... }
很不幸,由于 map_if_insertion_operator
没有考虑重载或局部特化的情况,导致没有办法扩展 pybind11::detail
名称空间以定义其重载或局部特化的实现,因此暂时我们只能被动接受这个内置的 __repr__
实现。
C++ 函数模板局部特化 C++ 并不支持函数模板的局部特化,如果有必要,则需要特殊的处理,可以参考下面为 map_if_insertion_operator
增加局部特化支持的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <map> #include <string> class C {};using MapString2String = std::map<std::string, std::string>;template <typename Map, typename Class_>struct map_repr_impl { static void _(Class_& cl, const std::string& name) { std::cout << "generic template\n" ; } }; template <typename Class_>struct map_repr_impl <MapString2String, Class_> { static void _(Class_& cl, const std::string& name) { std::cout << "partial specialization\n" ; } }; template <typename , typename , typename ... Args> void map_if_insertion_operator (const Args &...) { }template <typename Map, typename Class_>auto map_if_insertion_operator (Class_& cl, const std::string& name) -> decltype (std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void ()) { return map_repr_impl<Map, Class_>::_(cl, name); } int main () { C i; std::string name{"abc" }; map_if_insertion_operator<std::map<std::string, int >, C>(i, name); map_if_insertion_operator<std::map<std::string, std::string>, C>(i, name); return 0 ; }
参考资料 An introduction to C++’s SFINAE concept: compile-time introspection of a class member
https://jguegant.github.io/blogs/tech/sfinae-introduction.html
How to Make SFINAE Pretty
https://www.fluentcpp.com/2018/05/15/make-sfinae-pretty-1-what-value-sfinae-brings-to-code/
Choose between different implementations depending on type properties at compile time, in C++
https://medium.com/@mortificador/choose-between-different-implementations-depending-on-type-properties-at-compile-time-in-c-68e3fd5cd2f8
Notes on C++ SFINAE
https://www.bfilipek.com/2016/02/notes-on-c-sfinae.html
SFINAE decltype comma operator trick
http://skebanga.blogspot.com/2013/03/sfinae-decltype-comma-operator-trick.html
Function Templates Partial Specialization in C++
https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
Template Partial Specialization In C++
https://www.fluentcpp.com/2017/08/11/how-to-do-partial-template-specialization-in-c/
Controlling overload resolution
https://foonathan.net/blog/2015/10/16/overload-resolution-1.html
Modern C++ Features – decltype and std::declval
https://arne-mertz.de/2017/01/decltype-declval/
C++11 模板元编程
https://www.jianshu.com/p/b56d59f77d53