2

I am trying to replace all macros in my code with templates. I have a macro that binds this as the first argument to a function so that a member function can be used in a static context. How can I achieve that with templates?

I would like this functionality:

#define BIND_EVENT_FN(func) std::bind(&func, this, std::placeholders::_1)

Or with lambdas:

#define BIND_EVENT_FN(func) [this](Event& e){ return func(e); }

Something along the lines of this, but obviously not quite like this because this does not compile:

template<class T>
auto bind_event_fn(T& func)
{
    return [this](Event&& e){ return func(e); };
}

Minimal working example below, is it possible to replace the macro?

#include <functional>
#include <iostream>

#define LAMBDA_BIND_FN(fn) [this](const int& event) { return fn(event); }
#define BASIC_BIND_FN(func) std::bind(&func, this, std::placeholders::_1)

void run_in_static_context(std::function<void(const int&)> fn)
{
    fn(42);
}

class A {
  public:
    A()
    {
        run_in_static_context(LAMBDA_BIND_FN(A::member_fn));
    }
  private:
    void member_fn(const int& event)
    { 
        std::cout << "Event: " << event << std::endl;
    }
};

int main()
{
    A();
}
Rudolf Lovrenčić
  • 147
  • 1
  • 2
  • 9

2 Answers2

5

I am trying to replace all macros in my code with templates.

You can't. While there is a lot that templates can do, there is a lot of stuff that can only be handled by macros.

Templates are patterns that generate types, functions, or variables. As such, they must live within the rules of C++ types, functions, or variables. A (non-lambda) function definition cannot reach into the scope where it was declared and just pull stuff out. Like this for example. A function's scope is isolated; it cannot access things outside of its scope (outside of globals and members of classes).

Therefore, a template function cannot do that either. Two-phase lookup does allow templates to in some way be affected by the accessible names based on where they are instantiated, but this is extremely limited and doesn't help you at all in this case.

You can't make a template instantiation import this automatically from where the template was instantiated. You can pass this as a parameter to a function, and it can use that value in the bind expression, but the template can't reach out and find this on its own.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

A Curiously Recurring Template Pattern (CRTP) class template could help. Create a class template where you translate the macros that should work with this into member functions. You must then inherit it in the classes that should have access to any of the functions.

Your code with that in place:

#include <functional>
#include <iostream>

template<typename T>  // T = the child class
struct bind_helper {
    // put "class macros" in here, translated into functions

    template<class F>
    inline auto lambda_bind_fn(F func) {
        return // a lambda that will cast "this" to a "T*" (the class that inherited
               // bind_helper) then dereference "func" and call it
            [this, func](const int& e) { return (static_cast<T*>(this)->*func)(e); };
    }
};

void run_in_static_context(std::function<void(const int&)> fn) {
    fn(42);
}

class A : public bind_helper<A> { // inherit with the class itself as template parameter
public:
    A() { 
        run_in_static_context(lambda_bind_fn(&A::member_fn)); 
    }

private:
    void member_fn(const int& event) {
        std::cout << "Event: " << event << std::endl;
    }
};

int main() {
    A x;
}

Output:

Event: 42
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108