views:

510

answers:

9

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
}
+2  A: 

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.

Billy ONeal
What do you mean with "use a plain virtual function"? I cannot cast it to a function pointer, so...
Jaime Pardos
You can't call a virtual function from C code, because C doesn't know about virtual method tables.
Ori Pessach
@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.
Billy ONeal
**"[...]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.
Jaime Pardos
@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.
Billy ONeal
"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.
Jaime Pardos
@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
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.
Billy ONeal
@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.
Jaime Pardos
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.)
Rob Kennedy
@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)
Billy ONeal
A: 

Virtual functions are essentially function pointers under-the-hood. They just point to different functions for different classes. To simulate virtual-function behavior, have a function pointer stored somewhere, then to 'override' it just reassign it to some different function.

Alternatively, you might want to test this, but I think interfaces have pretty good binary compatibility. You might get away with exposing a C++ interface composed entirely of pure virtual functions, so long as all the parameters and return types have a consistent binary format (eg. C types). It's not a standard, but it might be portable enough.

AshleysBrain
Err... that's true, but how does that answer the OP's question? :)
Billy ONeal
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**.
Jaime Pardos
@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.
Ori Pessach
No problem, Ori. Can't see the advantages of deleting your comment, however, but still...
Jaime Pardos
+4  A: 

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.

Permaquid
Sadly, this is not the case, this functions signature doesn't haev a userData param.
Jaime Pardos
@Jaime: If you'd have added such facts to your question we would all have wasted less time.
Georg Fritzsche
@gf: Sorry, you're right.
Jaime Pardos
I guess i will for now be the one that nitpicks about the portability issues of using static member functions as C-callbacks: http://stackoverflow.com/questions/2068022/in-c-is-it-safe-portable-to-use-static-member-function-pointer-for-c-api-callb
Georg Fritzsche
Thank you again, gf. No problem, I'm stuck with windows in this. But it's something worth keeping in mind in the future.
Jaime Pardos
+2  A: 

Assuming that the C SDK allows you to pass it a void * to your data (and you should pass it your this pointer for the derived class:)

class Base {

  public:

    void Initialize() { /* Pass /this/ and a pointer to myFuncAGate to your C SDK */ }

    virtual myFuncA()=0;

    // This is the method you pass to the C SDK:
    static myFuncAGate(void *user_data) {
        ((Base*)user_data)->myFuncA();
    }
};


class Derived1: public Base {
  public:
    virtual myFuncA() { ... } // This gets called by myFuncAGate()
};

If the C SDK doesn't allow you to pass a pointer to your data which is then passed back to you through the callbacks, then you'll have a really hard time doing this. Since you indicated in one of your comments that this is indeed the case, you're pretty much out of luck. I would suggest using simple functions as callbacks, or overloading the constructor and defining multiple static methods. You'll still have a hard time determining what's the proper object your methods are supposed to work with when your callbacks are invoked by the C code.

If you post more details about the SDK it might be possible to give you more relevant suggestions, but in the general case, even with static methods, you need some way of obtaining a this pointer to work with.

Ori Pessach
Sorry, no luck, as I commented under Permaquid's answer :(
Jaime Pardos
+2  A: 

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
}
Eclipse
+1  A: 

The obvious way is like this, with FillPointers implemented in each derived class.

class Base
{
private:
    CStruct myStruct;
};

class Derived1 : public Base
{
 private:
    static FillPointers() { myStruct.funA = myFunA; myStruct.funB = myFunB; ...}
    Derived1() {  FillPointers();  }
    static myFunA(...) {...};
    static myFunB(...) {...};
};

However you can probably avoid that using some template magic...

Roddy
Thank you, I think this would be one of the best answers, if it hadn't missed the "template magic" details.
Jaime Pardos
+3  A: 

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."

Rob Kennedy
I guess i will for now be the one that nitpicks about the portability issues of using static member functions as C-callbacks: http://stackoverflow.com/questions/2068022/in-c-is-it-safe-portable-to-use-static-member-function-pointer-for-c-api-callb
Georg Fritzsche
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.
Rob Kennedy
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.
Georg Fritzsche
+1  A: 

If the C SDK wants you to perform operations without providing a userdata, then object-orientation is likely unnecessary and you should just write some functions. Else, time to find a new SDK.

DeadMG
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.
Jaime Pardos
A: 
class Base
{
    template<class T>
    FillPointers(T* dummy) { myStruct.funA = T::myFunA; myStruct.funB = T::myFunB; ...}
private:
    CStruct myStruct;
};

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

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

int main()
{
    Derived1 d1;
    Derived2 d2;
    // Now I have two objects with different functionality
}

see also http://stackoverflow.com/questions/1820477/c-static-virtual-members/2809789#2809789

Alsk