Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Template for pointer to member function of undetermined class, with different arguments, called as argument

I am not an advanced programmer. The short version of the problem would be: how can I make a template for a global function that calls a pointer to a member function of classes that are only known at runtime (and who have different kind of arguments)?

The function is global and its declaration is this:

template<class T>
const double gExtFunc(const double &x,  \
                      const double &y,  \
                      const double &ep, \
                      const double &es, \
                      // function pointer here
                      const double &a,     \
                      const double &k,  \
                      double &mid_OUT,  \
                      short  &i_OUT,    \
                      double &eps_OUT,  \
                      short  &trip_OUT);

This gets called inside some (virtual public) classes. For example (not using ellipsis):

class DerivedA: virtual public Base
{
public:
    void funcA(...)
    {
        // ...
        m_varA = gExtFunc(...);
        // ... 
    }
    // ...
    const double commonFunc(const double &a, const double &k) const;
};

const double DerivedA::commonFunc(const double &a, const double &k) const
{
    // ...
}

The Base class is just for common variables, mostly. The pointer to function points to commonFunc() which has the same name in all virtual classes, but different definitions and type of arguments in each. For example, the above DerivedA uses it with two const double& arguments, while a DerivedE with:

const double DerivedE::commonFunc(const int &n, const double &k) const;

Now, I managed to make it work with a lambda function (based on this, when I only had one m_varA), which meant the function pointer argument of gExtFunc() was this:

const double gExtFunc(..., std::function<const double(const double&, const double&)> fPnt, ...)

and it was called, for ex. inside DerivedA, as:

m_varA = gExtFunc(..., [this](const double &a, const double &k){ return commonFunc(a, k); }, ...)

and it worked, but only as long as it was called inside one class, only. As soon as I tried calling it in DerivedE, for m_varE, it, too, failed: was not declared in this scope, pointing to its definition of m_varE.

I have seen this answer, but that requires me to first create an object, which I can't do. So I tried to go around it and replace the pointer to function argument like this:

template<class T>  // first the template, above the function
const double gExtFunc(..., T *t, const double(T::*t)(const double&, const double&) fPnt, ...)

and, inside the definition: t->fPnt(...). But how would gExtFunc() be called? I tried this:

m_varA = gExtFunc(..., new DerivedA(), &DerivedA::commonFunc, ...)

but it fails, was not declared in this scope, same as above. I am stuck here. If there is an alternative to what I am trying to do, I'm very much willing to change it.


EDIT:

As a temporary, ugly solution, I copied the gExtFunc() to each class that needs it, trimmed & adjusted, including giving up pointer to function, just a direct call of the member function commonFunc(). There are only 3 cases, for now, but the future may bring more, which is why I consider this to be ugly and why the question is still valid.

like image 395
a concerned citizen Avatar asked Nov 08 '22 09:11

a concerned citizen


1 Answers

This solution would do it (AClass is a test class):

class AClass
{
    private:
        std::string s_;
        unsigned short a_;

    public:
        AClass(const std::string& __s, unsigned short __a) : s_(__s), a_(__a) {}

        void m_Test(unsigned short __a, const std::string& __s) 
        {
            a_ += __a;
            s_.append(__s);
        }

        unsigned short getA() { return a_; }
        std::string getS() { return s_; }
};

template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
{
    return (obj.*mf)(std::forward<Args>(args)...);
}

template <typename T, T> struct proxy;

template <typename T, typename R, typename ...Args, R(T::*mf)(Args...)>
struct proxy<R(T::*)(Args...), mf>
{
    static R call(T & obj, Args &&... args)
    {
        return (obj.*mf)(std::forward<Args>(args)...);
    }
};

int main()
{
    AClass a("I ", 2);
    proxy<void(AClass::*)(unsigned short, const std::string&), &AClass::m_Test>::call(a, 23 , "am: ");

    std::cout << a.getS() << a.getA() << "\n";

    std::cin.get();
    return 0;
}

I don't remember who wrote this, but it is someone from the forum. Thanks to him.

Edit

You could also use polymorphism mechanisms if you can make your commonFunc virtual. I don't know if this will suits your needs, but here is a sample:

class AClass;

double gExtFunc(AClass* __acl, const double& __a, const double& __b, const double& __f);

class AClass
{
    private:
        const std::string& aClass = "AClass";

    protected:
        double r_;

    public:
        AClass() : r_(0) {}

        void func_A()
        {
            r_ = gExtFunc(this, 2.0, 1.5, 300.011);
        }

        virtual double m_Test(const double& __a, const double& __b) 
        {
            return __a * __b;
        }

        double getR() { return r_; }
        void setR(const double& __r) { r_ = __r; }

        virtual const std::string& getClass() { return aClass; }
};

class BClass : virtual public AClass
{
    private:
        const std::string& bClass = "BClass";

    public:
        BClass() : AClass() {}

        void func_A()
        {
            r_ = gExtFunc(this, 3.0, 1.5, 311.128);
        }

        virtual double m_Test(const double& __a, const double& __b)
        {
            return __a * __b;
        }

        const std::string& getClass() { return bClass; }
};

double gExtFunc(AClass* __acl, const double& __a, const double& __b, const double& __f)
{
    double d = __acl->m_Test(__a, __b);
    return d * __f;
}

int main()
{
    AClass a;
    BClass b;

    a.func_A();
    b.func_A();

    std::cout << a.getR() << "\n" << a.getClass() << "\n\n";
    std::cout << b.getR() << "\n" << b.getClass() << "\n";

    std::cin.get();
    return 0;
} 
like image 112
Papipone Avatar answered Nov 15 '22 12:11

Papipone