1

My program is to evaluate and characterize time series information. There are about 90 distinct signals that the data may have. Each signal has a unique formula and distinct set of parameters and values. This code and my problem(s) with it is for loading these values from a config file. Compiler is VS 2010.

Each signal is represented by a class, here illustrated with the class TRI{}, and each such class derives from the class SIGNAL{}. SIGNAL contains a static map (my actual code uses unordered_map) which is to hold the pairs of signal names and pointers to the signal's member function which assigns the parameter values to their respective variables. My problem is with manipulating this member function.

Apparently, the address of the signal's member function, in this code &TRI::load_cfg_vals, is never stored in the map, sig_map. So it seems from the debugger. When I try to call the TRI signal's load function, the compiler says I'm trying to call something that's not a function. Please see the code for some of my failed attempts.

How can I get this to work with these objects? I really don't know what the problem is, and worse, I don't know what I'm not understanding about how to use STL or C++.

(I'm about ready to give up. I'm considering an alternative, more C-like approach. Using a map, associate each signal name with a unique integer (already in the actual code - they're all represented as unique single bits). Load each element of an array of void pointers with the address of the load function of the signal whose integer value is the offset into the array of that element. The first way I chose, the code below, seemed easier to maintain, a little more high-level.)

Among the many questions and answers I studied before posting this were

member function pointers and inheritance

C++ Map of string and member function pointer

C++ pointers to member functions

C++ Call pointer to member with a map from a const function

TIA

#include <iostream>
#include <map>
#include <string>
using namespace std;

typedef std::map< string, void *>    ARG_MAP;
typedef ARG_MAP::iterator            ARG_ITR;
typedef std::pair < ARG_ITR, bool>   ARG_PAIR;

// forward decl
class SIGNAL;

typedef int (SIGNAL::*PF)(void);
typedef std::map< string, PF>          SIG_MAP;
typedef SIG_MAP::iterator              SIG_MAP_ITR;
typedef std::pair < SIG_MAP_ITR, bool> SIG_MAP_PAIR;

class SIGNAL
{
public:
    ARG_MAP             arg_map;
    ARG_ITR             ai;
    ARG_PAIR            ap;

    static SIG_MAP      sig_map;

    SIGNAL() {};
    ~SIGNAL(){};
    virtual int calc() = 0;
    virtual int load_cfg_vals() = 0;
};

// tried globals versus members, no difference
SIG_MAP       SIGNAL::sig_map;
SIG_MAP_ITR   smi;
SIG_MAP_PAIR  smp;

class TRI: public SIGNAL
{
public:
    float f;

    int calc(){return 1;}
    int load_cfg_vals()
    {
        // the f arg
        ai = arg_map.find("f_descriptive_name");
        *(float *) ai->second = (float)12.005;

        return 1;
    };

    TRI()
    {
        // associates the TRI class function 'load_cfg_vals()' with the
        // signal name 'tri'
        SIGNAL::sig_map.insert(std::make_pair ("tri", 
                                               (PF) &TRI::load_cfg_vals));
        // this apparently doesn't load the address of the function, see below

        //sig_map.insert(std::make_pair ("tri",&TRI::load_cfg_vals));
        // fails with error C2440: 'initializing' : cannot convert from 
        // from 'int (__thiscall TRI::* )(void)' to 'PF '

        //SIGNAL::sig_map.insert( map<string, PF>::value_type("tri", 
        //      dynamic_cast< & SIGNAL::load_cfg_vals> (&TRI::load_cfg_vals) ));
        // C2059: syntax error : '&'
        // so, maybe this is right but for my lack of understanding of what
        // types are involved/required here

        // contains the list of descriptive names of the signal's parameters
        // and the addresses of the variables that hold the parameters'values
        arg_map.insert(std::make_pair ("f_descriptive_name", (void*) &f));
    };
    ~TRI(){};
};


int main(void)
{
    TRI    tri;
    PF     pf;
    char * input_str = "tri";  // this and the names of the many other
                               // signals would be read from the cfg file

    // while there are still more signal names to read in
    // while( fscanf(...input_str...) {  removed
        if( (smi = tri.sig_map.find (input_str)) == tri.sig_map.end())
            cout << "'" << input_str << "' not found\n";
        else
        {
            // smi->second is supposed to contain the function of the
            // signal class that is to properly interpret and handle
            // the list of values stored in the cfg file 
            //(smi->second)();
            // error C2064: term does not evaluate to a function taking
            //              0 arguments

            string s = smi->first;  // OK
            pf = (PF)smi->second;
            // Doesn't contain the address of the function that was
            // loaded, above, in TRI().  The debugger identifies
            // it as TRI::`vcall'{4}', I don't know what that is.
            // Debugger emits the entire type of the operator and 
            // its return value, but I can't get it to format for
            // proper display here.  If someone wants to see it, 
            // I'll supply it unformatted.

            //int z = (*pf)();
            // error C2064: term does not evaluate to a function taking 0 
            // arguments

            // the following don't help the value in pf.  same error C2064 or 
            // complaints about improper use of the casts
            //pf = reinterpret_cast <int (__thiscall *)(void)>(smi->second);
            //pf = static_cast <int (__thiscall *)(void)>(smi->second);

        }
    // } // end while   removed
  return 1;
}
Community
  • 1
  • 1
Shellsunde
  • 76
  • 10
  • Casts on function pointers and pointers-to-member and pointers-to-member-function are really bad, don't use them. If the compiler says the types are incompatible, figure out why, but never just stick in a cast on one of these types to shut the compiler up. – Ben Voigt Sep 04 '13 at 21:25

1 Answers1

0

Keep it simple, instead of trying to insert that pointer-to-member type into a map just try to do the conversion to the PF type:

PF pf = &TRI::load_cfg_vals;

This doesn't compile, for the reasons explained in an answer to one of the questions you linked to, just like this reduced example doesn't:

struct A {
  virtual int f() = 0;
};

struct B : A {
  int f() { return 0; }
};

int (A::*pf)() = &B::f;

So if that doesn't compile, your version that relies on that but in a more complicated situation, is not going to compile either.

Why can't you just do this instead:

    SIGNAL::sig_map.insert(std::make_pair ("tri", 
                                           &SIGNAL::load_cfg_vals));

The type of &SIGNAL::load_cfg_vals is the same type as you're trying to store in the map, so it works.

This doesn't compile because the template argument for dynamic_cast must be a type not a pointer-to-member:

    SIGNAL::sig_map.insert( map<string, PF>::value_type("tri", 
          dynamic_cast< & SIGNAL::load_cfg_vals> (&TRI::load_cfg_vals) ));

And dynamic_cast is for converting pointers to polymorphic types, not pointer-to-member types, this would compile instead, but it's better to avoid the cast:

    SIGNAL::sig_map.insert( map<string, PF>::value_type("tri", 
          static_cast<PF> (&TRI::load_cfg_vals) ));

Also, why are all your types and typedefs in ALL_CAPS? Stop shouting, ALL_CAPS is for macros, don't name your types like that.

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • You might want to mention that `&SIGNAL::load_cfg_vals` associates to a vtable slot, not a specific implementation, so it will end up finding `TRI::load_cfg_vals` when called. – Ben Voigt Sep 04 '13 at 21:35
  • Thank you, Jonathan and Ben. With respect to using caps for typedefs and types, I found it useful in my early years and grew accustomed to it, and no one complained and had me change it. Sorry if it irks you. – Shellsunde Sep 08 '13 at 02:49
  • Thank you, Jonathan and Ben. a) Thanks for pointing out that the earlier post answered at least part of my question. I did see that, but I didn't give it proper credence. b) However, even with your modification, smi->second() (in main()) still doesn't evaluate to a function. I've put the address of a vtable element in smi->second, as you said: How do I get to the function address in order to call it? c) With respect to using caps for typedefs and types, I found it useful in my early years and grew accustomed to it, and no one complained and had me change it. Sorry if it irks you. – Shellsunde Sep 08 '13 at 03:00
  • `smi->second` is a pointer-to-member, you can't just call a member function like a free function, you need an object. You need something like `(tri.*(smi->second))()` – Jonathan Wakely Sep 08 '13 at 12:37
  • Now I understand. My takeaway from this is a better appreciation of the design of the notion of a class, to wit, in C++, we want to keep a class's parts strictly where they belong, used by only those objects that have explicit rights to do so. Specifically, pointers into a class have to be bound (either at compile or run time) to the class type they want to engage. For my code, I'll resort to either functions at file scope or static member functions, both with external linkage, neither accessed via vtables. Virtual functions aren't needful for this load_cfg_vals operation. Thanx for your help. – Shellsunde Sep 08 '13 at 15:55