1

I've been trying to follow some of this advice:

https://softwareengineering.stackexchange.com/a/213635/46534
and
https://en.wikipedia.org/wiki/Function_object

But struggling to get it to compile.

I have a delegate definition:

struct SomeDelegate {
    void operator()(SomeType *data) {
        //do some stuff with data
    }
};

And then a member function which accepts a function pointer:

void DoSomethingThatCallsback(void(*callback)(SomeType *) ) {
    callback(ptrToSomeType);
}

And then when I try and use this member function as follows:

foo.DoSomethingThatCallsback(SomeDelegate());

I get the compile error:

cannot convert argument 1 from 'SomeDelegate' to 'void (__cdecl *)(Core::SomeType *)'

All the examples I've been reading suggest this is possible.

I've tried using templates like this:

template <typename Callback>
void DoSomethingThatCallsback(Callback callback)

But I get a similar error.

I'm ultimately looking for a more OOP approach to resorting to function pointers or moving to C++11. Also, unable to use the STL.

Updated with more context

struct MapOpenedDelegate {
public:

    void operator()(Map *openedMap) {

    }
};

class MapReader {
public:

    template <typename Callback>
    void RegisterMapOpenedCallback(Callback &callback) {
        _callbacks.Add(callback);
    }

private:
    Rise::List<void(*)(Map *)> _callbacks;
};
...
mapReader.RegisterMapOpenedCallback(MapOpenedDelegate());
Adam Naylor
  • 6,172
  • 10
  • 49
  • 69
  • you want to pass pointer to function which is operator? keep in mind operator can't be static so it can't be passed as a pointer to function – Sly_TheKing Jul 18 '17 at 11:09

3 Answers3

0

The first approach doesn't work because you tell your function to accept a function pointer while you pass it a SomeDelegate instance.

The template approach could indeed work, depending on the body of your template.

Example of a template that could work:

template <typename Callback>
void DoSomethingThatCallsback(Callback& callback) {
  callback(ptrToSomeType);
}

Note that this works given that ptrToSomeType is of type SomeType*. And the Callback type equals the SomeDelegate type like you defined it in your question, or any other type that provides an operator() that accepts a SomeType* as its one and only argument.

Update

I think there's no real reason for using templates here, given what you provided me with now.

First of all, in my previous answer I wasn't aware of the fact that these callback instances were to be stored in a list. This changes the situation completely.

I've assumed in the below solution to your problem that the caller of the RegisterMapOpenedCallback has ownership of the callback instances. The caller passes them by reference and the MapReader only stores pointers to them.

struct MapOpenedDelegate {
public:

    void operator()(Map *openedMap) {

    }
};

class MapReader {
public:

    void RegisterMapOpenedCallback(MapOpenedDelegate& callback) {
        _callbacks.Add(&callback);
    }

private:
    Rise::List<MapOpenedDelegate*> _callbacks;
};

In order to provide a perfectly clear solution I still miss context. I'm not quite sure what you want to achieve other than storing some callback instances. I do hope that the above helps you in some way though.

arjen1988
  • 109
  • 1
  • 5
  • When I add the template specification exactly as you've outlined, I get the same error. I must be doing something unusual.. – Adam Naylor Jul 18 '17 at 11:24
  • Is there a chance you post a bit more of your code, a bit more context? I'm sure we're missing something here. – arjen1988 Jul 18 '17 at 11:25
  • Thanks @Arjen I've updated the question with more of the concrete code. Note all of that is contained within a common namespace if that matters. – Adam Naylor Jul 18 '17 at 11:30
  • The first solution will not work because the type of Callback must be known at compile-time. If you want to store arbitrary delegates, you definitely need dynamic polymorphism (virtual calls)! – Tobias Ribizel Jul 18 '17 at 11:56
  • @TobiasRibizel Yeah, forget my first answer as I wasn't aware of the fact that he wants to store the delegates. I mean, I missed that. – arjen1988 Jul 18 '17 at 11:58
  • 1
    Ah, my attempt to provide the minimal set of code may be obfuscating the intention slightly, apologies. My goal isn't to store `MapOpenedDelegate`s, it's to store things that behave like them. In other words - lot's of functions will be registered that want to know when a map is opened, what they do when they are invoked will vary. – Adam Naylor Jul 18 '17 at 12:05
  • @AdamNaylor In that case you could adjust my answer with what TobiasRibizel suggests. Take his snippet and replace the `MapOpenedDelegate` in my example with his `MyDelegate`. – arjen1988 Jul 18 '17 at 12:09
  • I can try that but I'm keen to understand how the C++ example here works: https://en.wikipedia.org/wiki/Function_object#In_C_and_C.2B.2B, it doesn't appear to need to use polymorphism or inheritance. – Adam Naylor Jul 18 '17 at 12:11
  • 1
    @AdamNaylor The `std::sort` function is a template and the `IntComparator` stuct implements a `operator()(int, int)` which is required by the `std::sort` template in order to successfully compile. However, it's not storing the comparator anywhere though, in that sense it's totally different from what you want to achieve. Given that a begin and end iterator over a collection of integers is provided to the `std::sort` function, for each different type of comparator a distinct `std::sort` function is generated and compiled. – arjen1988 Jul 18 '17 at 12:20
  • I see the difference now. Thank you. In that case I will probably use an interface pattern and store objects that implement that. – Adam Naylor Jul 18 '17 at 12:22
  • The code in the answer isn't correct, you are capturing a temporary by non-const reference. Have tried to use it in a minimal, working example? – skypjack Jul 18 '17 at 14:11
  • @skypjack You're correct, I totally missed the last line. I'll remove that, because that's indeed incorrect usage. Capturing by (const-)reference and passing a temporary doesn't make any sense anyways. – arjen1988 Jul 18 '17 at 14:15
0

It looks to me like std::function could solve all of your problems:

A std::function<ReturnType(Parameters)> is able to encapsulate anything that behaves like a function with parameters Parameters and return type ReturnType, especially functors, lambdas and function pointers.

Thus replacing the function pointer by such a std::function should do the trick.

EDIT: If you are not allowed to use the full standard library (STL != standard library), you would probably have to implement the functionality yourself using a thin wrapper like

struct MyDelegate {
    virtual operator()(...);
}

and then implement it for all possible callable objects using dynamic polymorphism:

template <typename Callable>
struct MyDelegateImpl : MyDelegate {
    Callable m_f;
    virtual operator()(...) { m_f(...); }
}

Note that this is only a rough sketch, so assignment etc. might be a bit more problematic. Make sure to store the delegates in a std::unique_ptr so your objects don't get sliced

Tobias Ribizel
  • 5,331
  • 1
  • 18
  • 33
0

As far as I understood from your question and the comments to the answers, you want to create a container that erases the type of the listeners and call them through a callback method when needed. Of course, you don't want to use the standard library for unknown reasons, so lambdas and std::function are out of scope here.

So far, so good. It follows a minimal, working snippet based on your example code:

#include<vector>

struct Map {
    // You haven't provided a definition for this class
};

struct ADelegate {
    void operator()(Map *) {
        // ...
    }
};

struct AnotherDelegate {
    void operator()(Map *) {
        // ...
    }
};

class MapReader {
    using fn_type = void(*)(void*, Map *);

    struct Callback {
        void *ptr;
        fn_type fn;
    };

    template<typename T>
    static void proto(void *ptr, Map *map) {
        (*static_cast<T *>(ptr))(map);
    }

public:
    template <typename T>
    void attach(T *ptr) {
        callbacks.push_back({ ptr, &proto<T> });
    }

    void operator()() {
        Map map;

        for(auto &&cb: callbacks) {
            cb.fn(cb.ptr, &map);
        }
    }

private:
    // I used a vector for I don't know how Rise::List is implemented
    std::vector<Callback> callbacks;
};

int main() {
    MapReader mr;

    ADelegate d1;
    AnotherDelegate d2;

    mr.attach(&d1);
    mr.attach(&d2);

    mr();
}

I've used std::vector for I've not an implementation for Rise::List. I'm sure you can easily replace it with your own list type.
See the snippet above up and running on wandbox.

Let's see how it works.
Pretty much everything happens within the MapReader class. For you want to store callbacks that belong to different types, a possible solution is to type-erase the original types and put them in a void * box. Then you can get the type back when needed by adding an extra layer of indirection (the static template function proto) and do the right call.

As you can see, it just works and you can attach different types to the MapReader (be aware that you must guarantee the lifetimes of the listeners overcome the one of the emitter). The price you pay for that is the jump through an intermediate function that cast the void * to the right type before to do the call.

skypjack
  • 49,335
  • 19
  • 95
  • 187