5

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.

a concerned citizen
  • 787
  • 2
  • 9
  • 25
  • Are 10 function parameters really the best way implement this? – StoryTeller - Unslander Monica Sep 04 '16 at 11:49
  • @StoryTeller I have to include all cases, the 1st two of them are necessary, the next four are for `fPnt()` inside, depending on which `commonFunc()` is called from which class, then the last 4 are for debugging, only. I could give them up, but they do provide valuable information as the whole `gExtFunc()` has some iterations inside that adjust themselves, dynamically, based on `x` and `y`. So, currently, 10 it is... :-) – a concerned citizen Sep 04 '16 at 12:00

2 Answers2

1

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;
} 
Papipone
  • 1,083
  • 3
  • 20
  • 39
  • 1
    This goes a bit over my head, but what I see is that I create a function based on a struct that holds templates for `commonFunc()`'s arguments, such that when I create an `AClass` object, it will be able to call whichever function from whichever class, based on arguments. Do I come anywhere close? If yes, this implies I have to make an object, which I can't. Nothing in what I'm doing takes place inside `main()`. `gExtFunc()` is global, `DerivedA`&co are classes, all `commonFunc()` are member functions of these, and `m_varA`&co are also member variables of these classes. To top it all, (cont) – a concerned citizen Sep 04 '16 at 12:14
  • ...`gExtFunc()` needs to call `commonFunc()` for each `m_varA`, m_varE`, etc, because it depends on it. If it matters, `gExtFunc()` is some root finding algorithm, while all member functions `commonFunc()` are polynomial functions of varying complexity. Some are as simple as trigonometric functions, others involve more complex functions. Thank you for the answer, though, I'll try to see maybe I can adapt it? But first I have to understand it. – a concerned citizen Sep 04 '16 at 12:17
  • "it will be able to call whichever function from whichever class, based on arguments" That's it. – Papipone Sep 04 '16 at 12:52
  • But will I be able to do it without creating an `AClass` object? Or, how could I mix the above to fit inside one of `gExtFunc()`'s arguments? Actually, I think I got it: instead of a pointer to function as argument I use `AClass &aclass`, then use it inside `gExtFunc()` as `class->commonFunc()`, instead of `fPnt()`. This might work, I'm on it now. – a concerned citizen Sep 04 '16 at 12:58
  • Can you make your commonFunc virtual ? – Papipone Sep 04 '16 at 13:40
  • I could, all classes that have `commonFunc()` are `virtual public` because there's a diamond sort in there, with many facets. Something like `Base > DerivedA > LastClass`, `Base > DerivedB > LastClass`, etc. What would that accomplish? But there's another problem now: `gExtFunc` needs to call `commonFunc()` inside in different ways for the root finding algorithm. I think my whole problem got very ugly now. – a concerned citizen Sep 04 '16 at 13:58
  • That's not a bad solution, but, as I mentioned, I don't get to be in `main()`. `m_varA`&co belong to `DerivedA`&co, they're initialized within a member function (say `fncA()`&co), which gets called by the constructor from `LastClass`. Still, my last problem remains: the pointer inside `gExtFunc()` needs to be called as either `fPnt(x,y)-es*ep` for `DerivedA`, or as `fPnt(x,y)/fPnt(x,y/k)-ep/es` for `DerivedE`, etc, which means I have to make some other global function with a `switch/case`... It's ugly, but mostly I'm sorry I can't give more details for such a complicated program. – a concerned citizen Sep 04 '16 at 16:16
0

Feel free to downvote my answer if it is wrong. I am just trying to help. I don't have the mental capacity to read all of that and understand your exact question, but I think I understand the basis of your problem. I am assuming you are doing something like making a function where an argument can vary like:

template <typename T> func(T arg1){}

and somewhere else in code you are expecting that T to be an int or a char * or whatever but you are having trouble converting T to the proper type.

What I would do is work in memory. My function would be something like

enum types {
    OBJECT,
    INT,
    STRING
};

int type;
int addrOfType;

void func(int addr, int typ) {
    type = typ;
    addrOfType = typ;
}

and to call it do something like

object argument;
func((int)&argument,OBJECT);

and somewhere else in the code you are retrieving the data

switch (type) {
    case OBJECT:
        object obj = *(object*)addrOfType;
        //do whatever with obj
        break;
}

I hope that helped. It looked like a tough question so I figured I would attempt to try and help as I sit here bored. Feel free to neg my answer if it is terrible. It is so hard to post on mobile.

tpunt
  • 2,552
  • 1
  • 12
  • 18
gopro_2027
  • 103
  • 1
  • 9
  • I don't think this would work, thanks for the answer, though. In short, what I have is a member variable, `m_varA`, or `m_varE`, depending on class, calling an external, global function, `gExtFunc()`, who takes a pointer to member function as argument. This member function is `commonFunc()`, it has the same name in all classes that have it, but its definition and argument types differ from class to class. `DerivedA` might use it with `(double,double)`, `DerivedE` with `(int,double)`. I can get over this by making all use `(double,double)` and pass an `int` instead, where needed, but in rest... – a concerned citizen Sep 04 '16 at 06:31
  • I think if you specify the arguments as (unsigned int) and use &argument you can parse it as the type of variable you want it to be like this: `void commonFunc(unsigned int arg1, unsigned int arg2) { double a = *(double*)arg1; int b = *(int*)arg2; } double q = 23458; int r = 3426; commonFunc(&q,&r);` – gopro_2027 Sep 11 '16 at 06:26