从 std::function 继承(尽管已有建议)

inherit from std::function (despite the existing advice)

我已经阅读了为什么从 std::function 继承是一个坏主意的原因。我可以忍受没有虚拟析构函数:在我的程序中只有一个地方需要删除 subclass,而且我确切地知道它在哪里,所以我很乐意进行手动删除。

如果我可以访问 target(),我根本不需要这样做 - 我需要 "wrapped" 函数的地址,以便我可以在容器中再次找到它。我正在使用 Arduino IDE 1.8.3(c++11 标准)并以 NodeMCU、Wemos D1、Sonoff 等为目标……硬件不足以说明内存非常有限。因此 stdlib 是 "cut-down" 版本。例如,编译器标志不指定 RTTI,并且 - 当然 - target() 等仅在存在 RTTI 时才编译。我取消了它,我什至无法编译,因为生成的可执行文件对于可用 RAM 来说太大了...

关键是我不需要花哨的类型信息,只需要可调用目标的物理地址。

我的想法是继承自 std::function(应用程序中使用的唯一函数签名是 function<void()>,重写构造函数,捕获目标地址并提供一个函数(不是称为 target() 以免混淆)以在适当的时间检索它 - "getFn()" 也许......这就是我已经在用我自己的 "task" class 做的并且它有效很好。我需要将它扩展到 std::function,但我看不出我该怎么做,除非我可以访问 "target" 地址!

最后,排除任何试图提供帮助的回复,例如建议将 "handle" 返回到首先创建此函数的用户例程,然后让他们使用句柄再次调用以查找容器条目...我绝对不想那样做。我为自己的背部制作了一根杆,因为我的 API 必须避免接收任何把手,然后使用它们恢复原样。这不能改变,所以我不是在寻找替代方案,因此缺乏确切实施的更多细节。

我只是寻求重新继承自 std::function 的建议。

总而言之,我想我的问题是:考虑到我认为,是否有任何其他原因导致这可能不起作用我可以处理我已经阅读过的问题吗?

这就是我的想法:

 class XFunction: public function<void()> {
  public:
    uint32_t  fn;
    XFunction(void (&_fn)() ): function<void()>(_fn) {
      Serial.printf("CTOR %08x fn=%08x\n", this,_fn);
      fn=reinterpret_cast<uint32_t>(&_fn);
   }
   uint32_t getFn(){
    return fn;
   }
};

capture the address of the target

立即失效的地址。或者至少可能无效。请记住:std::function 存储包装函数的 copy;它不通过引用存储它。因此,如果您存储指向提供的可调用 T 的指针,function 将从该 T 复制并在内部存储副本。所以你的指针不会指向内部对象。

哦当然,如果 T 是一个实际的函数指针,你可以存储函数指针,它总是有效的(DLL/SOs 放在一边)。但是如果 T 是一个函子,你存储的指针可能指向一个堆栈对象或其他形式的 "going to eventually be destroyed" 东西。它的生命周期不受 std::function.

控制

更不用说 std::function 也适用于成员指针,不能 转换为通用 void*。事实上,函数指针是否可以转换为 void* 和返回是实现定义的。

所以不,你想要的一般来说是不合理的。如果您不想使用仿函数和成员指针,您就不会使用 std::function;你只是拿一个裸函数指针。如果您想为 function 分配某种唯一标识符,则必须采用不同的方式。

std::function 继承不是你的问题。


The thing is (and it may sound daft) but I don't really care what it points to - I'm never going to dereference it or call it or do anything to it at all except know its value so I can find it again

但您仍然需要它唯一,对吗?考虑一下:

XFunction some_func()
{
  int x = ...;
  return XFunction([x]{...});
}

lambda 临时文件的位置很可能在堆栈上。这意味着此函数的两次独立调用很可能会为 lambda 临时函数使用相同的堆栈地址。因此,尽管 x 可能具有不同的值,但您的系统会将这些单独的 XFunction 类 视为相同的实例。


The essence of what I'm trying to do is: 1) create function object say F1, pass it to my process which stores it in a container ...2) I then want to find that item in the container using only F1 (or attributes thereof).

您通常不能对可调用对象执行此操作。一般来说,函数对象没有任何类型的可比性、相等性或可确定的唯一标识的概念。一个特定的函数对象有一个地址,是的。但是如果你(或用户)复制那个仿函数,你可能希望它们"compare"等于或产生相同的"hash",是吗?

嗯,函子不会那样做。虽然特定的函子可能提供相等性或同一性,但这些不是一般的 属性 函子。如果没有这样的 属性,就无法将它们转换成一些可测试的整数值。

鉴于你的设计,你想要的是行不通的。您有两个选择:

  1. 限制自己使用函数指针。

  2. 限制自己使用引用。也就是说,您根本不使用 std::function。您改为将类型擦除的 指针 存储到您将调用的函数 pointer/functor/member 指针。你不拥有该对象,你不复制它,nada。

    这意味着同一性不是基于概念上的平等,而是基于相同的对象。因此,如果有人向您注册了一个 lambda,然后 returns 它被复制给其他人,它 将不会 被认为等于注册的函数。

    这也意味着用户必须管理注册到该系统的任何函数对象的内存和生命周期。这意味着 std::bind/mem_fn 风格的东西将 永远不会 在这样的系统中工作,因为这些类型需要接收者长期存储它们,而你的接收者只存储一个指针.

这些是您唯一的选择。