0

I'm working on a library in C++ that is sort of a higher-level socket interface. The idea is to have similar calls with the same function names, but with a cleaner interface and better type safety. That means that I have my own (networking) bind function, which has a signature something like:

void MySocketLib::bind(const MySocketLib::MySocket &s, const MySocketLib::MyEndpoint &e);

Where MySocketLib is a namespace. This all works fine, so long as you call

MySocketLib::bind(s,e);

When s and e have the matching types. But, if you do something like this:

#include <functional>
#include "mysocketlib.h"
using namespace std;
using namespace MySocketLib;
...
bind(s,e);

Then, the bind picks up the std::bind in functional. I can always just call as MySocketLib::bind(s,e), but I would prefer if the un-qualified call picked up the MySocketLib::bind function instead of the std::bind template. This is using gnu g++.

It seems a similar problem between std::bind and ::bind has been addressed in the gcc headers. But it doesn't fix my problem, since my sockets are not ints.

What I've tried is to create a specialization, like this:

namespace std {
template<>
void bind(const MySocketLib::MySocket &s, const MySocketLib::MyEndpoint &e) {
    MySocketLib::bind(s,a);
} }

But it tells me that my bind(...) does not match any template declaration. (Full compiler message below). Even though these are the exact types as in the call from which it happily instantiates std::bind. What I would like is either some advice on how on earth I can specialize std::bind, or some other way to accomplish making the plain bind() refer to MySocketLib::bind instead of std::bind when using both namespaces.

Following is the full compiler diagnostic from g++ for my failing specialization. Any advice appreciated.

In file included from mysocketlib.cpp:12:0:
mysocketlib.h:330:6: error: template-id 'bind<>' for 'void std::bind(const MySocketLib::MySocket&, const MySocketLib::MyEndpoint&)' does not match any template declaration
 void bind(const MySocketLib::MySocket &s, const MySocketLib::MyEndpoint &a) {
      ^~~~
In file included from mysocketlib.h:323:0,
                 from mysocketlib.cpp:12:
/usr/include/c++/7/functional:899:5: note: candidates are: template<class _Result, class _Func, class ... _BoundArgs> typename std::_Bindres_helper<_Result, _Func, _BoundArgs>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^~~~
/usr/include/c++/7/functional:875:5: note:                 template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^~~~
T W Bennet
  • 383
  • 1
  • 7
  • 3
    Perhaps you should take some time to read [Why is “using namespace std” considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) It's also mostly valid for other namespaces as well, don't blindly go `using` them (not even your own). – Some programmer dude Jan 03 '18 at 07:47
  • I think the question is how to protect against library users that don't follow that advice. – SoronelHaetir Jan 03 '18 at 07:51
  • 4
    @SoronelHaetir I don't think you should. If a user wants to shoot him or herself in the foot, then you let him or her do it. If you provide a library, you provide the library as-is. It's up to the users of the library to use it properly, it's not really something that should or even *can* be forced. – Some programmer dude Jan 03 '18 at 08:04
  • The main goal is to create an interface similar to plain C sockets, but with fewer oddities and land mines, particularly for use by networking students. Using namespaces is something folks do, and it fails silently. The code compiles, runs without generating any run-time messages or exceptions, and the expected network bind just doesn't happen. Really won't do – T W Bennet Jan 03 '18 at 18:54
  • @TWBennet If you want to support people who ignore namespaces by stuffing all symbols into the global one, then you have to use C-style naming and give everything a globally unique name over all projects in the entire world. So it becomes `namespace MySocketLib { template<> MySocketLib_notanamespacecoloncolon_bind`. Now users who `using `namespace MySocketLib;` and `using namespace std;` will no longer have a name collision. And yes, this is stupid and eliminates the entire point of namespaces, but that is what you have asked for. You could also use a less ugly name. – Yakk - Adam Nevraumont Jan 03 '18 at 19:32
  • And no, you may not specialize template functions in namespace std and change their meaning. That makes your program ill-formed, no diagnostic required, even if you succeed. There are narrow situations where specializing a template in namespace std is permitted, and this is not one of them. The user, by `using namespace std;` and `using namespace MySocketLib;` has *requested that any symbols in both collide*. The absolute best you could do is make the call ambiguous. Wait, bind uses varargs... ok, you can win. – Yakk - Adam Nevraumont Jan 03 '18 at 19:34

1 Answers1

0

See comments above why this is engineering a solution akin to "customers keep on knocking the bottom floor off my buildings, and the upper floors fall down. How can I ensure the top floors still stand when the bottom floor is knocked out?"

std::bind is implemented using varardic forwarding templates.

namespace MySocketLib {
  namespace details {
    void my_socket_bind(const ::MySocketLib::MySocket &s, const ::MySocketLib::MyEndpoint &e);
  }
  template<class S0, class S1,
    std::enable_if_t<
      std::is_same< std::decay_t<S0>, MySocket >{}
      && std::is_same< std::decay_t<S1>, EndPoint >{}
    , bool> = true
  >
  void bind(S0&&s, S1&e) {
    return details::my_socket_bind( s, e );
  }
}

this is fragile, arcane, but should place your bind at or above any std::bind when both are imported into the same scope.

You implement MySocketLib::details::my_socket_bind.

In pure :

enable_if_t<...> may have to be replaced with typename enable_if<...>::type.

is_same<...>{} may have to be replaced with is_same<...>::value.

Some older compilers may choke at my SFINAE code despite it being compliant. There are many alternatives, which ones work depend on the exact compiler.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thank you. I was able to get this working, using your c++11 suggestions. I think it's pretty cool to levitate the top n-1 floors of a building. And I'll think about this some more. – T W Bennet Jan 04 '18 at 06:05