Alternative to c++ static virtual methods

52,126

Solution 1

You can make Base be a class template that takes its function pointers from its template argument:

extern "C" {
struct CStruct
{
  void (*funA)(int, char const*);
  int (*funB)(void);
};
}

template <typename T>
class Base
{
public:
  CStruct myStruct;
  void FillPointers() {
    myStruct.funA = &T::myFunA;
    myStruct.funB = &T::myFunB;
  }
  Base() {
    FillPointers();
  }
};

Then, define your derived classes to descend from an instantiation of Base using each derived class as the template argument:

class Derived1: public Base<Derived1>
{
public:
  static void myFunA(int, char const*) { }
  static int myFunB() { return 0; }
};

class Derived2: public Base<Derived2>
{
public:
  static void myFunA(int, char const*) { }
  static int myFunB() { return 1; }
};

int main() {
  Derived1 d1;
  d1.myStruct.funA(0, 0);
  d1.myStruct.funB();
  Derived2 d2;
  d2.myStruct.funA(0, 0);
  d2.myStruct.funB();
}

That technique is known as the curiously recurring template pattern. If you neglect to implement one of the functions in a derived class, or if you change the function signature, you'll get a compilation error, which is exactly what you'd expect to get if you neglected to implement one of the pure virtual functions from your original plan.

The consequence of this technique, however, is that Derived1 and Derived2 do not have a common base class. The two instantiations of Base<> are not related in any way, as far as the type system is concerned. If you need them to be related, then you can introduce another class to serve as the base for the template, and then put the common things there:

class RealBase
{
public:
  CStruct myStruct;
};

template <typename T>
class Base: public RealBase
{
  // ...
};

int main()
  RealBase* b;
  Derived1 d1;
  b = &d1;
  b->myStruct.funA(0, 0);
  b->myStruct.funB();
  Derived2 d2;
  b = &d2;
  b->myStruct.funA(0, 0);
  b->myStruct.funB();
}

Beware: Static member functions are not necessarily compatible with ordinary function pointers. In my experience, if the compiler accepts the assignment statements shown above, then you can at least be confident that they're compatible for that compiler. This code isn't portable, but if it works on all the platforms you need to support, then you might consider it "portable enough."

Solution 2

I think you just need to use a plain virtual function. A static virtual function does not make sense, because a virtual function is resolved at runtime. What's there to resolve when the compiler knows exactly what the static function is?

In any case, I would suggest leaving the existing function pointer solution in place if possible. Baring that, consider using a normal virtual function.

Solution 3

I still can see a use for static virtual methods, here an example:

class File
{
    static virtual std::string extension()  {return "";}
}

class ExecutableFile : public File
{
    // static because every executable has same extension
    static virtual std::string extension()  {return ".exe";}
}


std::string extension = "";

// needing static
extension = ExecutableFile::extension();

// not needing static nor virtual
ExecutableFile exeFile;
extension = exeFile.extension();

// needing virtual
File* pFile = &exeFile;
extension = pFile->extension();

Solution 4

A common pattern when passing a function pointer (a callback) to a C SDK uses the fact that many such functions allow a void * parameter that is "user data". You can define your callbacks to be simple global functions, or static class member functions. Then each callback can cast the "user data" parameter to a base class pointer so you can call a member function that does the work of the callback.

Solution 5

You could just pass the functions directly into the base class constructor:

class Base
{
    Base()(int (*myFunA)(...), int (*myFunB)(...)) 
    { myStruct.funA = funA; myStruct.funB = myFunB; ...}
private:
    CStruct myStruct;
};

class Derived1 : public Base
{
    Derived1() : Base (myFunA, myFunB) {}
    static myFunA(...) {...};
    static myFunB(...) {...};
};

class Derived2 : public Base
{
    Derived2() : Base (myFunA, myFunB) {}
    static myFunA(...) {...};
    static myFunB(...) {...};
};

int main()
{
    Derived1 d1;
    Derived2 d2;
    // Now I have two objects with different functionality
}
Share:
52,126
raven
Author by

raven

Updated on September 06, 2020

Comments

  • raven
    raven over 3 years

    In C++ is not possible to declare a static virtual function, neither cast a non-static function to a C style function pointer.

    Now, I have a plain ol' C SDK that uses function pointers heavily.

    I have to fill a structure with several function pointers. I was planning to use an abstract class with a bunch of static pure virtual methods, and redefine them in derived classes and fill the structure with them. It wasn't until then that I realized that static virtual are not allowed in C++.

    Also this C SDKs function signature doesn't have a userData param.

    Is there any good alternative? The best I can think of is defining some pure virtual methods GetFuncA(), GetFuncB(),... and some static members FuncA()/FuncB() in each derived class, which would be returned by the GetFuncX(). Then a function in the abstract class would call those functions to get the pointers and fill the structure.

    Edit Answering to John Dibling, it would be great to be able to do this:

    class Base
    {
        FillPointers() { myStruct.funA = myFunA; myStruct.funB = myFunB; ...}
    private:
        CStruct myStruct;
        static virtual myFunA(...) = 0;
        static virtual myFunB(...) = 0;
    };
    
    class Derived1 : public Base
    {
        Derived1() {  FillPointers();  }
        static virtual myFunA(...) {...};
        static virtual myFunB(...) {...};
    };
    
    class Derived2 : public Base
    {
        Derived2() {  FillPointers();  }
        static virtual myFunA(...) {...};
        static virtual myFunB(...) {...};
    };
    
    int main()
    {
        Derived1 d1;
        Derived2 d2;
        // Now I have two objects with different functionality
    }
    
  • Billy ONeal
    Billy ONeal about 14 years
    Err... that's true, but how does that answer the OP's question? :)
  • raven
    raven about 14 years
    What do you mean with "use a plain virtual function"? I cannot cast it to a function pointer, so...
  • raven
    raven about 14 years
    Sadly, this is not the case, this functions signature doesn't haev a userData param.
  • Ori Pessach
    Ori Pessach about 14 years
    You can't call a virtual function from C code, because C doesn't know about virtual method tables.
  • raven
    raven about 14 years
    The OP understands quite well what a virtual function is, and understands that it isn't possible to do what he wanted with them. That's why he asked for an alternative.
  • raven
    raven about 14 years
    Sorry, no luck, as I commented under Permaquid's answer :(
  • Billy ONeal
    Billy ONeal about 14 years
    @Jaime Pardos: Which function a virtual function actually calls is not known, unless the type to which that virtual function calls is known. If you want your callback function to be virtual, sorry, but there's no way to do that. Given though that forcing a cast to a function pointer is the reason you are using static in the first place, this is not a plug in solution. But given that your original question did not contain that information I think my interpretation is a reasonable response.
  • raven
    raven about 14 years
    "[...]Now, I have a plain ol' C SDK that uses function pointers heavily. I have to fill a structure with several function pointers. I was planning to use an abstract class with a bunch of static pure virtual methods, and redefine them in derived classes and fill the structure with them. It wasn't until then that I realized that static virtual are not allowed in C++." I'd say it was pretty clear.
  • Billy ONeal
    Billy ONeal about 14 years
    @Jaime: I see nothing about passing virtual functions into that C sdk, nor do I see anything about casting those static virtual functions into function pointers.
  • Georg Fritzsche
    Georg Fritzsche about 14 years
    @Jaime: If you'd have added such facts to your question we would all have wasted less time.
  • raven
    raven about 14 years
    "I have to fill a structure with several function pointers [...] static pure virtual methods [...] redefine them in derived classes and fill the structure with them" I'll accept your answer if you want the points so badly but, dude, lrn2read.
  • Ori Pessach
    Ori Pessach about 14 years
    @Jaime - OK, sorry for being snide. I had the vague, half baked notion that anyone could reach a working solution to your problem just by understanding the underlying principals and thinking about it for a few minutes. I since took a stab at it myself and find that I was wrong - there are subtleties here that aren't obvious. I was rude, and I apologize. I'll remove the comment since it is unhelpful.
  • Billy ONeal
    Billy ONeal about 14 years
    @Jaime: I don't see the word cast, or any mention of passing into the C callback. You're saying you want derived classes to use different function pointers. I don't expect you to accept this answer, but I want to help you ask better questions. Say what you want to do ("pass virtual functions into a C API which only accepts function pointers") instead of a possible way you think you might be able to do this ("I want a static virtual function"). The fact that the function is static tells me absolutely nothing about what you want to do with it.
  • Billy ONeal
    Billy ONeal about 14 years
    For what it's worth, I read the above assuming you had a C API which used function pointers which you wanted to refactor into C++ virtual functions.
  • raven
    raven about 14 years
    No problem, Ori. Can't see the advantages of deleting your comment, however, but still...
  • raven
    raven about 14 years
    @Billy: Ok, now I see what's going on there. When I wrote "fill the structure with them" I really meant "put them in the function pointers of the structure", not "fill the structure FROM them". The API is a third party library so I can't touch it. I could argue why it was implicit if you carefully read all the question but it's pretty pointless now. I thank you for your comments, anyway, and will try to keep them in mind in order to ask better questions in the future. To show you I am not resentful or anything, I will accept your answer.
  • raven
    raven about 14 years
    Thank you, I think this would be one of the best answers, if it hadn't missed the "template magic" details.
  • raven
    raven about 14 years
    I'm stuck with the SDK. About the other part of your answer, interesting answer. I think it's wrong, however, in some sense of "wrong"; object-orientation is ALWAYS unnecesary, but I felt it would be interesting to encapsulate everything in a class, and, having different sets of functions for different functionality, build a class hierarchy to make everything as easy to use and maintainable as my skill allows. I will think about this.
  • Georg Fritzsche
    Georg Fritzsche about 14 years
    I guess i will for now be the one that nitpicks about the portability issues of using static member functions as C-callbacks: stackoverflow.com/questions/2068022/…
  • Georg Fritzsche
    Georg Fritzsche about 14 years
    I guess i will for now be the one that nitpicks about the portability issues of using static member functions as C-callbacks: stackoverflow.com/questions/2068022/…
  • Rob Kennedy
    Rob Kennedy about 14 years
    When I first posted my answer, I hadn't considered that perhaps the assignment from static member function to function pointer only worked for me because neither was extern C. I merely figured that if my C++ compiler accepted the assignment statements, then they were at least compatible on my system. I've since gone back and made the struct extern C, and the code still works. Tested on Sun 5.8 and GNU 3.4.6 and 4.1.2. All three compile and run without warnings or errors.
  • raven
    raven about 14 years
    Thank you again, gf. No problem, I'm stuck with windows in this. But it's something worth keeping in mind in the future.
  • Georg Fritzsche
    Georg Fritzsche about 14 years
    It might work on most compilers now, but i think it should at least be mentioned that it can break instead of implying that using static member functions is perfectly fine.
  • Rob Kennedy
    Rob Kennedy about 14 years
    Billy, I'm not sure the text of your answer really answers the question. Since it's the accepted one, could you please edit it so that it incorporates what came out in the comments. (Comments aren't shown by default for old questions, and I wouldn't want the answer to be buried in hidden comments.)
  • Billy ONeal
    Billy ONeal about 14 years
    @Rob Kennedy: I would do that if I had any idea why he accepted this answer. (Personally I don't think I deserve the checkmark here)
  • Albert Tomanek
    Albert Tomanek over 5 years
    Thank you! Just the answer I was looking for.