3

I had an interesting idea of creating a type of vector table using functors. However, after further analysis it would seem that this would be a pipe dream due to the fact that it would be prohibitively expensive since the entire table would have to be generated to pass on some of the function parameters from the function that calls the functor. The functor in this case would act as a proxy to manage parameter differences between functions that the functor is calling.

Confused yet? :-) Example:

#include <functional>

class classA {};
void fn1(int a);
void fn2(classA& a);

void fn_x(int fn_to_call, int a1, classA& a2) {
  std::function<void()> vtable[] = {
    [&]{ fn1(a1); },
    [&]{ fn2(a2); }
  };
  vtable[fn_to_call]();
}

void fn_y(int fn_to_call, int a1, classA& a2) {
  switch(fn_to_call) {
    case 0:
      return fn1(a1);
    case 1:
      return fn2(a2);
  }
}

For brevity, I skipped checks as I'm doing this on a phone.

So as I understand it, fn_x would have to create an array of functors upon entering the function, and then call the one required. Whereas fn_y would just jump to the one required.

When the number of functions to dispatch is small, this isn't much of a problem, but as the number increases, it becomes worse and worse.

If I could somehow defer the creation of the functor objects until it it's called, that would be optimal. Is that possible?

Edit also, how will a compiler handle this? Will it result in multiple calls stack entries (a call to the functor and then a call to the target function) or would it just be as efficient as the switch?

Adrian
  • 10,246
  • 4
  • 44
  • 110
  • where is `function` defined? – Oswald Oct 09 '13 at 14:18
  • 1
    @Oswald: I'd bet on `std`. – Jan Hudec Oct 09 '13 at 14:18
  • @Adrian: If they were plain function pointers, the array could be static (note, that lambda that captures nothing is convertible to plain function pointer of appropriate type). Than it would be simply written into the executable by the compiler with no runtime overhead whatsoever. – Jan Hudec Oct 09 '13 at 14:21
  • Are you aware of the Dispatch Table concept? – Nick Louloudakis Oct 09 '13 at 14:27
  • 3
    You don't need to bind the parameters, you can also just accept them as further parameters: `[](int a1, classA){ fn1(a1); }` or `std::bind(fn1, _1)` - and the second example works even nicer: `std::bind(fn2, _2);`. :) The call would then simply be `vtable[idx](a1, a2);`. That way, you don't commit to any specific parameters and can have a static table. – Xeo Oct 09 '13 at 14:31
  • @Oswald, `function` is defined in the `std` namespace in the `` header. It's incomplete as I was on a phone. – Adrian Oct 09 '13 at 14:35
  • @JanHudec, yes I'm aware of that, but then you couldn't remap the function parameters. – Adrian Oct 09 '13 at 14:36
  • @NickL, are you are referring to an actual vtable using function pointers? That would require the same function signature, which I deliberately shown as different. – Adrian Oct 09 '13 at 14:38
  • @Adrian: You could remap them. You can still use lambdas. You'd just have to pass everything that might be needed as arguments and avoid capturing. – Jan Hudec Oct 09 '13 at 14:42
  • @Xeo, that's was what I was looking for. Post it as an answer and I'll give you cred. – Adrian Oct 09 '13 at 14:42
  • @Adrian, ok, but what if you tried making an actual dispatch table using template parameters? – Nick Louloudakis Oct 09 '13 at 14:43
  • (@Adrian: well, I generally meant the same as Xeo; just note, that `bind` can't return plain function pointers, you need to use lambdas for that) – Jan Hudec Oct 09 '13 at 14:44
  • @NickL., can you explain further? – Adrian Oct 09 '13 at 14:44
  • @Xeo, doing what you suggested wouldn't be any more efficient then just having the signature of `fn1` and `fn2` take both parameters though and just use function pointers, would it? In fact, it should be worse. I don't think that the optimizer would be smart enough to make as efficient as the `switch`. Am I right? – Adrian Oct 09 '13 at 14:48
  • @Adrian: It will save you the hassle of actually making all functions take the same parameter. Also, did you actually profile and care about the performance difference, if there even is any? – Xeo Oct 09 '13 at 14:55
  • @Xeo, I do care, but no, I've not profiled. It would be enough for me that most compilers would result in code as efficient as the `switch` technique. – Adrian Oct 09 '13 at 15:01
  • I can not provide code right now, but please take an idea from here: http://stackoverflow.com/questions/3727858/templated-function-pointer My point was using those templated function pointers in a combination with a dispatch table. – Nick Louloudakis Oct 09 '13 at 15:13
  • @NickL., this doesn't look like anything more than a specific example of a lambda/bind functor. When you have a chance, write something up to try and explain further. – Adrian Oct 09 '13 at 15:19

1 Answers1

2

To take and summarize some of the recommendations in the comments, why not create just one set of functors that knows how to forward arguments?

#include <functional>

class classA {};
void fn1(int a) {}
void fn2(classA& a) {}

void fn_x(int fn_to_call, int a1, classA& a2) {
  using namespace std::placeholders;
  static std::function<void(int, classA&)> vtable[] = {
    std::bind(&fn1, _1),
    std::bind(&fn2, _2),
  };
  vtable[fn_to_call](a1, a2);
}

int main()
{
  classA a;
  fn_x(0, 3, a);
  fn_x(1, 3, a);
  return 0;
}
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • How will a compiler handle this? Will it result in multiple calls stack entries (a call to the functor and then a call to the target function) or would it just be as efficient as the switch? – Adrian Oct 09 '13 at 15:55
  • That is completely up to the compiler. You'll have to try it and see. – Dark Falcon Oct 09 '13 at 15:58