#Is there a C++-equivalent for the double-column operator in Java?

29 messages · Page 1 of 1 (latest)

worn zephyr
#

Java has a ::-operator that allows you to automagically create the equivalent of a std::function calling a non-static member function. For an example, see below

// One file
public class Test
{
    public void test()
    {
        // Do stuff
    }
}
// Another file
public class Main
{
    public static void main(String... args)
    {
        Test testObject = new Test();
        // The Java-equivalent of a std::function<void()>
        // is being created by the expression below.
        Runnable runnable = testObject::test;
    }
}

Now, my question is: Is there also an equivalent operator of some sorts for this behavior in C++? Sure, I know that you can just use the assign operator and put the name of the target method on the right side, but this does sadly not work for non-static member functions. Is there an operator that I can use to make it work for non-static member functions as well?

kindred impBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question run !howto ask.

worn zephyr
#

Is there a C++-equivalent for the double-column Java operator?

#

Is there a C++-equivalent for the double-column operator in Java?

full totem
#

you can achieve the same behaviour with lambdas but you might run into lifetime issues

#include <iostream>

struct S
{
    int i;
    void say_i()
    {
        std::cout << i << '\n';
    }
};


auto ohno()
{
    S s{2};
    auto f = [&](){ s.say_i();};
    f();  // 2
    s.i += 4;
    f(); // 6
    return f;
}

int main() 
{
    auto f = ohno();
    f(); // violent death
}
#

I dont code in java so Im not sure we are talking about the same thing btw

worn zephyr
full totem
#

you can bind lambdas like that to std::function

#

which is still a wrapper but you dont have to write it

kindred impBOT
#

@worn zephyr Has your question been resolved? If so, run !solved :)

full totem
#

you could also capture by value to avoid lifetime issues but then the s in the lambda is completely distinct from the s in ohno

oak void
#

You should probably make the lambda accept an object instead of creating a temporary one

#

Iirc that’s how Java would work as well

full totem
#

oh yea if it accepts an object then thats just a member function ptr

worn zephyr
full totem
#

got a small example of what it should look like in C++? you can leave out parts as well

worn zephyr
#

The thing that currently comes closest to my goal is this function that I wrote:

template<typename T, typename... A, typename M = void(T::*)(A... args)>
std::function<void(A...)> bind(M method, T* tPtr)
{
    return [tPtr, method](A... args) {(tPtr->*method)(args...);};
}

Then you can just use it like this:

int main()
{
    Test t {};
    t.state = "This is an example state";
    std::function<void()> func = bind(&Test::doStuff, &t);
    func();
}
worn zephyr
full totem
#

how do you make sure func doesnt outlive t?

worn zephyr
#

In my code, the bind statement would be in a class, I would use this instead of &t, and I would unsubscribe from the event before the object at which this is pointing is being deleted.

#

But yeah, this bind function is rather dangerous in its nature.

full totem
#
#include <iostream>
#include <functional>
#include <unordered_map>


template<class F>
struct Event
{
    std::unordered_map<int, std::function<F>> functions;
    int new_id;

    int add_callback(std::function<F> f)
    {
        functions[new_id] = std::move(f);
        return new_id++;
    }

    bool remove_callback(int id)
    {
        return functions.erase(id);
    }

    void trigger_event(int i)
    {
        for (auto& kv : functions)
        {
            kv.second(i);
        }
    }
};

struct Test
{
    void listener(int doStuff)
    {
        std::cout << "listener says: " << doStuff << '\n';
    }
};

int main()
{
    Event<void(int)> event {};
    Test test {};
    auto id = event.add_callback([&](int i){test.listener(i);});
    event.trigger_event(2);
    if (event.remove_callback(id))
    {
        std::cout << "Removed callback\n";
    }
    else std::cout << "what\n";
    event.trigger_event(2);
}
#

instead of += I used add_callback because += canonically returns a reference to *this

#

but since you want to remove it again you need a way to find it in the Event

#

thats the id

#

I still dont like how coupled this feels tho

#

but since the event should outlive all registered functions it should be fine

worn zephyr
#

Alright, thanks for your help on this topic. I actually had the Event class implemented before I posted in this forum (it uses UUIDs as a key), but you gave me the final thought-provoking impulse to solve the issue stated in the title, and you also notified me of the lifetime problems. Thanks 😄