4

I have a typedef:

typedef S32(iMyDataClass1::*getDataFunction_t)(void);

and a type:

struct functionMap_t {
    std::vector<getDataFunction_t> pDataFunctionTable;
    struct dataRequestor_t dataRequestor;
};

I then have a member variable:

functionMap_t FnMap1;

Which I then set up in an member initializer list:

myClass::myClass() : FnMap1({ 
 {
     &iMyDataClass1::getData1,
     &iMyDataClass1::getData2,
     &iMyDataClass1::getData3
   },
   dataRequestor1
 })

Which I then use in the constructor:

addNewFnMap(FnMap1);

This all works fine. Unfortunately, it's not extendable for function pointers to different classes, as getDataFunction_t is bound to pointers in iMyDataClass1

I tried using templates, but ran into issues that I could not get around. Now I'm trying to use std::function, which means I change the original typedef into (I think):

typedef std::function<S32(void)> getDataFunction_t;

What is the correct way to set up my initializer list in this situation? Do I use std::bind here?

Update: Here is my attempt at a new initializer list. I have no idea if this is correct, but this is what I had right before posting this question:

{ 
  {
    (std::bind(&iMyDataClass1::getData1, functionToFetchCorrectObject())),
    (std::bind(&iMyDataClass1::getData2, functionToFetchCorrectObject())),
    (std::bind(&iMyDataClass1::getData3, functionToFetchCorrectObject()))
  },
  PGN65370
}
Jeff Lamb
  • 5,755
  • 4
  • 37
  • 54
  • How are you going to use your pointers-to-members to different classes? They're different types, you can't just have a vector with both. – Barry May 11 '15 at 21:28
  • It's a vector of `std::function`s. Here's a link with someone detailing a usage of this (without any code, though): https://coderwall.com/p/_3vacg/use-std-function-for-all-your-function-passing-needs – Jeff Lamb May 11 '15 at 21:32
  • None of that even approaches answering the question. You want "function pointers to different classes." That means different types. How do you expect that to look internally? – Barry May 11 '15 at 21:35
  • I thought something like `std::bind(&iMyDataClass1::getData1)` would work, but I couldn't get things to compile. – Jeff Lamb May 11 '15 at 21:35
  • The link I posted hints that this is possible. I've updated my question with the code I was attempting to use. – Jeff Lamb May 11 '15 at 21:39
  • Do you want your class to have functions that call members of **one single** class type or of **multiple** class types? Nowhere in that link messages multiple class types, but your question suggests that you need that - so the link is irrelevant. – Barry May 11 '15 at 21:41
  • @JeffLamb what you are looking for is `std::mem_fn` instead of `std::bind`. Or as the answer by @5gon12eder suggests, lambdas. – PeterT May 11 '15 at 21:42
  • Honestly, my design was influenced (and hindered) by the constraint of calling members from one class. Multiple class types would be preferred, as that simplifies my design. – Jeff Lamb May 11 '15 at 21:46
  • I almost don't have the heart to tell you guys... My "attempt" above actually worked. The signature to functionToFetchCorrectObject was incorrect. Once I fixed that, everything worked. – Jeff Lamb May 11 '15 at 22:41

2 Answers2

3

You could use lambda expressions. However, if you want to keep the signature as std::function<S32(void)>, you'll need to capture your object.

myClass::myClass() : FnMap1({
  {
     [this](){ return getData1(); },
     [this](){ return getData2(); },
     [this](){ return getData3(); },
  },
  dataRequestor1
})

If you don't want to do this, you could change the signature to pass the this pointer as a parameter to your callbacks. But as Barry points out in the comments, this would not allow you to put callbacks to different classes into the same vector. You could reinterpret_cast the pointer inside the lambda but besides from being ugly and dangerous, how would the caller know what pointer to pass in?

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • While my original implementation using `std::bind` works, yours uses lambdas, which is faster than `std::bind`. Thanks! – Jeff Lamb May 11 '15 at 22:43
  • I don't think lambdas will produce faster code here but I'm glad you liked it. I prefer using lambdas for their elegance and expressiveness. – 5gon12eder May 11 '15 at 22:53
  • I haven't actually determined which is faster (I'll do this once the refactor is complete). However, since I'm using a compiler-supported part of c++11 instead of the `std::bind` library, is that a safe assumption to make? – Jeff Lamb May 11 '15 at 22:55
  • I'd expect both solutions to generate the same machine code but I couldn't tell for sure without actually trying it with a given compiler. – 5gon12eder May 11 '15 at 23:10
  • 1
    It will not be faster with lambdas, because you end up wrapping them in std::functions, which contain an indirection that is not easily resolvable at compile time (like function pointers, and unlike lambdas and functors). To get this performance boost, you'd need to let the templates propagate outwards instead of erasing the type with the std::function, but this gets intense quickly. – Nir Friedman May 12 '15 at 01:26
  • 1
    I confirmed. They're exactly the same speed using GCC 4.9.2. – Jeff Lamb May 14 '15 at 00:28
2

If you want it to work with different classes, you can simply store callables with whatever signature you want:

struct functionMap_t {
  std::vector<std::function<S32(void)>> pDataFunctionTable;
  struct dataRequestor_t dataRequestor;
};

Next, what I'd probably do is give your class higher order functions that return std::functions with the correct signature.

std::function<S32(void)> iMyDataClass1::getData1Getter() {
  auto f = [this] () {return this->getData1()};
  return f;
}

Now you can initialize pretty simply:

iMyDataClass1 o;
...

FnMap1({o.getData1Getter(), ..., dataRequestor1});

This code hasn't been checked carefully and I'm sure it contains syntax errors. But it should give you the gist of it. The main idea is: if you want to work with functions that may or may not be attached to particular objects, then you need to work with functions (really, closures themselves). Working with functions in this kind of way motivates a design that uses higher order functions, to returns functions from one context and pass them to another.

EDIT: I recently had some design conversations with someone about the Observer pattern in C++11. I recommended doing something quite similar to this, it can be really nice avoiding use polymorphism, to keep things generic and decoupled, and to avoid polluting inheritance hierarchy.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72