Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncompile-able results due to namespacing #236

Closed
zwimer opened this issue Oct 8, 2022 · 5 comments
Closed

Uncompile-able results due to namespacing #236

zwimer opened this issue Oct 8, 2022 · 5 comments

Comments

@zwimer
Copy link
Contributor

zwimer commented Oct 8, 2022

TLDR: binder sees the stream operator << in some namespace, B, then tries to define __str__ using << without referencing B.

Consider:

#include <pybind11/pybind11.h>
#include <ostream>

struct A { int a; };

namespace B {
	std::ostream & operator<<(std::ostream & a, const A & b) { return a; }
}

Where a.conf is just +class A. We end up with

// File: unknown/unknown.cpp
#include <ios>
#include <locale>
#include <ostream>
#include <sstream> // __str__
#include <streambuf>
#include <string>

#include <functional>
#include <pybind11/pybind11.h>
#include <string>
#include <pybind11/stl.h>


#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_unknown_unknown(std::function< pybind11::module &(std::string const &namespace_) > &M)
{
	{ // A file: line:8
		pybind11::class_<A, std::shared_ptr<A>> cl(M(""), "A", "");
		cl.def( pybind11::init( [](){ return new A(); } ) );
		cl.def_readwrite("a", &A::a);

		cl.def("__str__", [](A const &o) -> std::string { std::ostringstream s; s << o; return s.str(); } );
	}
}

If you try to compile this, it will fail because << will not be in scope, since the << operator was defined in the namespace B.

Possible solution: Change binder to be explicit with A::operator<<(s, o) (I like this idea; it is ugly, but this code is autogenerated so that's fine). One other benefit is this would make it clear which << operator is being called in the case that multiple are defined at different scopes.

@zwimer
Copy link
Contributor Author

zwimer commented Oct 8, 2022

I'll add this bug to the known bugs PR: #229

@lyskov
Copy link
Member

lyskov commented Oct 10, 2022

Thank you for reporting this @zwimer ! I just pushed 7b47e78 which should fix this.

@lyskov lyskov closed this as completed Oct 10, 2022
@hogabrie
Copy link

hogabrie commented Oct 25, 2022

Hi @lyskov,

7b47e78 causes a new issue for me.

If I add the following to test/T07.class.hpp:

#include <ostream>
namespace operators{
  struct Operators
  {
    friend std::ostream & operator<<(std::ostream & s, const Operators & a) { return s << "Hi"; }
  };
}

, test compilation fails:

/home/ghottiger/git/binder/build/test/T07_class.cpp: In lambda function:
/home/ghottiger/git/binder/build/test/T07_class.cpp:104:105: error: ‘operator<<’ is not a member of ‘operators’; did you mean ‘std::operator<<’?
  104 | ors::Operators const &o) -> std::string { std::ostringstream s; operators::operator<<(s, o); return s.str(); } );
      |                 

Even though this is perfectly ok C++ code. E.g. run this minimal example:

#include <iostream>

namespace operators {
  struct Operators
  {
    friend std::ostream & operator<<(std::ostream & s, const Operators & a) { return s << a.greetings(); }
    std::string greetings() const { return "Hello"; }
  };
}

int main() {
  operators::Operators s;
  std::cout << s << std::endl;
  return 0;
}

hogabrie added a commit to ANYbotics/binder that referenced this issue Oct 25, 2022
Revert "using fully qualified insertion operator name when binding `__str__` (fixing RosettaCommons#236)"

This reverts commit 7b47e78.
@lyskov
Copy link
Member

lyskov commented Oct 26, 2022

@hogabrie ah, - i had encounter this or very similar issue before, it became particularly obvious when class and friend function is templated (issue is not specific to operator<<). Solution that i used is to separate declaration of function from it definition. It is a bit verbose but so far this is the only solution that seems to work. Please try following example, it have worked for me:

namespace aaaa {
struct T
{
    friend std::ostream & operator<<(std::ostream & s, T const &);
};

inline std::ostream & operator<<(std::ostream & s, T const &) { return s << "hi..."; }
}

@hogabrie
Copy link

hogabrie commented Nov 4, 2022

Thank you @lyskov for the quick support and the amazing binder project. For now I reverted the commit in our fork. When I find the time I will adapt our huge templated library with this proposal to get the bindings working with the latest binder version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants