【问题标题】:I'm trying to make a networking bind function which matches before std::bind我正在尝试创建一个在 std::bind 之前匹配的网络绑定函数
【发布时间】:2018-06-12 21:05:34
【问题描述】:

我正在开发一个 C++ 库,它是一种高级套接字接口。这个想法是使用相同的函数名称进行类似的调用,但具有更清晰的接口和更好的类型安全性。这意味着我有自己的(网络)绑定函数,它的签名类似于:

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

其中 MySocketLib 是一个命名空间。这一切都很好,只要你打电话

MySocketLib::bind(s,e);

当 s 和 e 有匹配的类型时。但是,如果你这样做:

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

然后,绑定在函数式中获取 std::bind。我总是可以调用为 MySocketLib::bind(s,e),但如果非限定调用拾取 MySocketLib::bind 函数而不是 std::bind 模板,我会更喜欢。这是使用 gnu g++。

似乎 std::bind 和 ::bind 之间的 similar problem 已在 gcc 标头中解决。但这并不能解决我的问题,因为我的套接字不是整数。

我尝试过创建一个专业化,如下所示:

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

但它告诉我我的 bind(...) 不匹配任何模板声明。 (下面的完整编译器消息)。尽管这些是它愉快地实例化 std::bind 的调用中的确切类型。我想要的是一些关于我到底如何可以专门化 std::bind 的建议,或者是其他一些方法来完成使普通的 bind() 在使用两个命名空间时引用 MySocketLib::bind 而不是 std::bind。

以下是 g++ 对我失败的专业化的完整编译器诊断。任何建议表示赞赏。

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)
     ^~~~

【问题讨论】:

  • 也许你应该花点时间阅读Why is “using namespace std” considered bad practice? 它对其他命名空间也基本有效,不要盲目地去using 它们(甚至不是你自己的)。
  • 我认为问题是如何防止不遵循该建议的图书馆用户。
  • @SornelHaetir 我认为你不应该这样做。如果用户想射他或她自己的脚,那么你让他或她去做。如果您提供库,则按原样提供库。图书馆的用户可以正确使用它,这不是真正应该甚至可以强制的东西。
  • 主要目标是创建一个类似于普通 C 套接字的接口,但具有较少的怪异和地雷,特别是供网络学生使用。使用命名空间是人们会做的事情,但它会默默地失败。代码编译、运行时不会生成任何运行时消息或异常,并且不会发生预期的网络绑定。真的不会
  • @TWBennet 如果你想通过将所有符号填充到全局符号中来支持忽略命名空间的人,那么你必须使用 C 风格的命名,并为整个项目中的所有项目赋予一个全局唯一的名称世界。所以它变成了namespace MySocketLib { template&lt;&gt; MySocketLib_notanamespacecoloncolon_bind。现在using namespace MySocketLib;` 和using namespace std; 的用户将不再发生名称冲突。是的,这很愚蠢,并且消除了命名空间的全部意义,但这就是您所要求的。你也可以使用一个不那么难看的名字。

标签: c++ c++11 g++ std bind


【解决方案1】:

请参阅上面的 cmets,为什么要设计一个类似于“客户不断地从我的建筑物上敲下底层,而上层倒塌。当底层被敲掉时,我如何确保顶层仍然站立? "

std::bind 使用可变转发模板实现。

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 );
  }
}

这是脆弱的,神秘的,但是当两者都被导入相同的范围时,应该将你的bind 放在任何std::bind 之上或之上。

你实现MySocketLib::details::my_socket_bind

:

enable_if_t&lt;...&gt; 可能必须替换为 typename enable_if&lt;...&gt;::type

is_same&lt;...&gt;{} 可能必须替换为 is_same&lt;...&gt;::value

尽管我的 SFINAE 代码符合 标准,但一些较旧的编译器可能会卡住它。有很多替代方案,具体取决于具体的编译器。

【讨论】:

  • 谢谢。使用您的 c++11 建议,我能够完成这项工作。我认为悬浮建筑物的顶部 n-1 层非常酷。我会再考虑一下。
猜你喜欢
  • 2015-07-14
  • 2018-07-07
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多