0

I have two classes, Sim and IElement. The class IElement defines a function pointer, whereas the class Sim has a vector of IElement pointers. What is the correct way to call the pointed-to function from the function pointer defined in IElement, given that I have a vector of IElement*?

Or in another words, I have

std::vector<IElement*> nodes;

And I need to call a pointed-to function from IElement:

nodes[i]->*SetInput(); // ERROR: Identifier "SetInput" is undefined

I assume I'm having this error because nodes is a vector of pointers, and I am unaware of how to dereference nodes[i] before calling its pointed-to function.

Thank you for any advice.

A bit more detailed snippets of the code are given below.

The method of the Sim class where I am having the error Undefined identifier for the method of IElement

#include <vector>
#include "Elements.h" // defines class IElement in namespace Elements
void Sim::CreateNodes(int N) // this method belongs to the Sim class in namespace "Simulations"
{
    nodes = std::vector<Elements::IElement*>(N);
    int i = 0;
    while (i < N)
    {
        nodes[i] = new Elements::IElement(true); // for the sake of the example
        nodes[i]->*SetInput(); // ERROR: Identifier "SetInput" is undefined
        i++;
    }
}

whereas in the Elements namespace, I have the class IElement declaration

class IElement
{
public:
    typedef void(IElement::*SetInputFc_ptr)();
    IElement(bool); // normalizeInput
    ~IElement();
    SetInputFc_ptr SetInput;
};

and the class IElement implementation

IElement::IElement(bool normalizeInput)
{
    if (normalizeInput)
    {
        this->SetInput= &(this->SetInput_Sum);
    }
    else
    {
        this->SetInput= &(this->SetInput_Const);
    }
}
0

3 Answers 3

2

You need to both get the SetInput member value from the IElement object, using a normal member operator, and then call the member function on the IElement object, using ->*. Assuming you want to use the same IElement for both:

(nodes[i]->*(nodes[i]->SetInput))();

Or perhaps rewrite the couple of statements as:

Elements::IElement* node = Elements::GetElement(i);
nodes[i] = node;
(node->*(node->SetInput))();

By the way, &(this->SetInput_Sum) is not an officially valid way to get a pointer to member. If your compiler accepts it, it's allowing it as an extension. The IElement constructor should be written:

IElement::IElement(bool normalizeInput)
{
    if (normalizeInput)
    {
        this->SetInput= &IElement::SetInput_Sum;
    }
    else
    {
        this->SetInput= &IElement::SetInput_Const;
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for pointing out the extra mistake... I had not tried to compile the full code by the time I posted the question, and the error I posted was reported by Visual Studio (configured to compile with Intel compiler)
1

This also seems like an XY Problem. C++ has classes that mean you can avoid the if in the first place by subclassing and having a normalized and non-normalized version of same. Override SetInput accordingly.

That plays out roughly like this:

class IElement
{
public:
    IElement();
    virtual ~IElement();

    virtual void SetInput();
};

class IElementNormalized : IElement {
    IElementNormalized();
    virtual ~IElementNormalized();

    virtual void SetInput();
};

Object-Oriented design principles generally steer towards using classes to do a lot of the work for you, avoiding the need for individual classes to differentiate like this.

You could always capture the boolean used on the constructor and just reference that as a property when performing whatever operations you need to, doing it procedurally within each function needing to know.

4 Comments

An excellent point, assuming SetInput doesn't need to change to a different function during any one object's lifetime.
@aschepler Exactly! Let the vtable do all the work for you.
In the real program, IElement is a primitive class as well, such that many other classes inherit from it; Thus it seems to me that having a virtual SetInput would force me to declare two of each derived classes as well, one implementing IElement and the other implementing IElementNormalized. I am not an expert in design patterns though :)
C++ supports multiple inheritance, so it's not always a big deal. You could also use a template class where the implementation of SetInput differs depending on parameters and you pass in a normalization class to use. It depends on how performance sensitive this code is. Something like IElement<INormalizer> x and so on.
1

Simple example of calling method through pointer.

#include <vector>

class IElement
{
    public:
        void action() {}
};

using MemberPtr = void (IElement::*)();    
MemberPtr Action = &IElement::action;

int main()
{
    std::vector<IElement*>  nodes{1, new IElement};

    (nodes[0]->*Action)();
    // Note the braces around the expression before the call.
    // This is because `()` binds tighter than `->*` so you need the
    // extra braces to force the `->*` to bind first so you can then call
    // the resulting method.

    //  nodes[0]->*Action();  // This will not compile.
}

1 Comment

maybe because I use typedef inside the IElement class, this fails with the same error in my case (reported by Visual Studio, I had not tried to compile)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.