-1

I have read the reasons why inheriting from std::function is a bad idea. I can live with no virtual destructor: there is only one place in my program where the subclass needs to be deleted, and I know exactly where that is, so I'm happy to do the manual delete.

I wouldn't need to do this at all if I could access target() - I need the address of the "wrapped" function so I can find it again in a container. I'm using Arduino IDE 1.8.3 (c++11 standard) and targeting NodeMCU, Wemos D1, Sonoff etc...hardware is not THAT relevant suffice to say that memory is very limited. thus the stdlib is a "cut-down" version. For example, the compiler flags specify no RTTI and - of course - target() etc are only compiled in if RTTI is present. I un-hacked that and I couldn't even compile as the resultant executable was too big for the available RAM...

The thing is I don't need fancy typeinfo, just the physical address of the target callable.

My idea was to inherit from std::function (for the only function signature used in the app which is function<void()>, override the constructor, capture the address of the target and provide a function (NOT called target() so as not to confuse) to retrieve it at the appropriate time - "getFn()" maybe...It's what I'm already doing with my own "task" class and it works fine. I need to extend it for std::function but I can't see how I can do it unless I can access the "target" address!

Finally, to preclude any trying-to-be-helpful replies that suggest e.g. returning a "handle" to the user routine that creates this function in the first place and then having them call again with the handle to find the container entry...I absolutely don't want to do it that way. I have made a rod for my own back as my API must avoid receiving any handles and then using them to get back to the original. That can't change, so I'm not looking for alternative scenarios, hence the lack of more detail of the exact implmentation.

I'm only asking for advice re inheriting from std::function.

In summary then, I guess my question is: are there any other reasons why this might not work, given that I think I can handle the problems I have already read about?

This is what I had in mind:

 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;
   }
};
  • 2
    Why are you using `std::function` at all? `XFunction` *only works* with function pointers. So why not just take a `void(*)()` and be done with it? The whole point of `std::function` is that it can take *any callable*. – Nicol Bolas Aug 23 '17 at 16:48
  • 1
    FYI: The type you're looking for is `std::uintptr_t`, not `std::uint32_t`. – Nicol Bolas Aug 23 '17 at 16:51
  • Same question, you have this bit about not taking a *handle* but don't really explain what that means. If you take a reference to a function, is that not taking a handle? What does a `void(*)()` not do that you need? – Ryan Haining Aug 23 '17 at 16:53
  • @Nicol that's not "std::uint32_t" its the Arduino definition of 32-bit unsigned int. – Phil Bowles Aug 23 '17 at 16:59
  • @Ryan - yes, the function reference is a "handle" of sorts - but the problem is...I can't get it back since target() doesn't exists for std::lfunction on my platform - I'm looking for exactly that: a way to get hold of the "handle" inside the std::function object... – Phil Bowles Aug 23 '17 at 17:02
  • @Nicol - I "get" that about functions - but if something is callable, it has an address. All I want to do is know what that address is! I don't care what type of callable it is, just something unique about it and its runtime address is that unique thing. – Phil Bowles Aug 23 '17 at 17:15
  • @PhilBowles: "*if something is callable, it has an address.*" That doesn't mean it has a *unique* address. See the edits on my answer. – Nicol Bolas Aug 23 '17 at 17:33
  • @PhilBowles perhaps if you could explain what functionality you need from `std::function` you need that you cannot get from a `void(*)()` we could provide more helpful answers. – Ryan Haining Aug 23 '17 at 17:39
  • @Ryan - I am passing a function object whose target is e.g. bind(myfunc,42). For reasons known to folk wiser than me, this seems to cause no problems when the object that "wraps" it is type function. What I want to know is the runtime address of the runnable thing that bind returns before I stuff it into my function object which gets put into another wrapper object in a container. When that is a naked function whose address I know, I can find the wrapper in the container (who called myfunc?") by looking for a match on function address. How do I do the same if its NOT a naked function? – Phil Bowles Aug 23 '17 at 17:52

1 Answers1

4

capture the address of the target

An address that will immediately become invalid. Or at least potentially invalid. Remember: std::function stores a copy of the wrapped function; it doesn't store it by reference. So if you store a pointer to the provided callable T, function will copy from this T and store the copy internally. So your pointer won't point to the internal object.

Oh sure, if T is an actual function pointer, you can store the function pointer, and it will always be valid (DLL/SOs aside). But if T is a functor, the pointer you store may be to a stack object or some other form of "going to eventually be destroyed" thing. Its lifetime is not controlled by std::function.

Not to mention the fact that std::function works with member pointers too, which cannot be converted into a generic void*. Indeed, whether function pointers can be converted into void* and back is implementation-defined.

So no, what you want is not reasonable in general. And if you didn't want to be able to take functors and member pointers, you wouldn't be using std::function; you'd just be taking a naked function pointer. If you want to assign some kind of unique identifier to a function, you'll have to do it a different way.

Inheriting from std::function is not your problem.


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

But you still need it to be unique, right? Consider this:

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

The location of the lambda temporary is something on the stack, in all likelihood. Which means that it is very possible that two independent invocations of this function will use the same stack address for the lambda temporary. And therefore, despite potentially having different values for x, your system will consider these separate XFunction classes to be identical instances.


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).

You cannot do that for callable objects in general. Function objects, in general, have no concept of comparability, equality, or determinable unique identity of any kind. A specific function object has an address, yes. But if you (or the user) copy that functor, you presumably want them to "compare" equal or to produce the same "hash", yes?

Well, functors don't do that. While a specific functor may provide equality or identity, these are not a general property of functors. And without such a property, there is no way to convert them into some testable integer value.

Given your design, what you want cannot work. You have two options:

  1. Restrict yourself to function pointers.

  2. Restrict yourself to function references. That is, you don't use std::function at all. You instead store a type-erased pointer to the function pointer/functor/member pointer you will invoke. You don't own the object, you don't copy it, nada.

    This means that identity is not based on conceptual equality, but being the same object. So if someone registers a lambda with you, then returns it by copy to someone else, it will not be considered equal to the registered function.

    This also means that the user must manage the memory and lifetime of any function objects registered to this system. And it means that std::bind/mem_fn-style things will never work in such a system, since those types require the receiver to store them long-term, and your receiver only stores a pointer.

These are your only options.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks. Hmmmm. Damn. Is there any way I can get back the "real" target() function without incurring the prohibitive RTTI data ? – Phil Bowles Aug 23 '17 at 16:51
  • 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 – Phil Bowles Aug 23 '17 at 16:56
  • @PhilBowles Look at `target()` and `target_type()` at http://en.cppreference.com/w/cpp/utility/functional/function – Deduplicator Aug 23 '17 at 16:57
  • "@PhilBowles Look at target() and target_type() " - how will this help me? target() and target_type() are unavailable in my environment - I already thought I made that clear - that was my first port of call! – Phil Bowles Aug 23 '17 at 17:06
  • @Nicol - again I "get" that about lambdas. If it's also true for callables inside function objects, then I'm stuffed! 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). Each F1, F2 etc will be called by a timer. I want to find the timer that is calling say, F1 so I can cancel the timer. So, startTimer(F1) ...then stopTimer(F1) . I don't want to return the timer handle in the "startTimer" call... works fine for naked functions! – Phil Bowles Aug 23 '17 at 17:59
  • @PhilBowles: "*If it's also true for callables inside function objects*" Lambdas *are* function objects, so that's true of function objects in general. Indeed, it's *more* true of function objects in general, since you cannot know what's going on inside them. Also, the more detailed explanation of your problem that you provided here ought to go into your *question*, not as a comment. And see my edit for the answer. – Nicol Bolas Aug 23 '17 at 19:05
  • @Nicol - thanks for your time and effort on this - unfortunately I was coming to a similar conclusion (albeit from a position of far less knowledge!) . I have decided to restrict the user API to functions only. Callables inside my own routines will be restricted only to those I never need to find again - such as "fire and forget" timeres that will always run. I do have a "nuclear" method of stopping ALL timers - by clearing the queue! i don't think I'm going to get a better answer than yours. Thanks again. – Phil Bowles Aug 23 '17 at 19:13