On Dec 5, 1:02 pm, Michael Oswald <muell...@gmx.n etwrote:
I'm working on a project where I came across some situations,
where the GUI library works with normal C callbacks.
You really should be more specific about what you mean by a
"normal C callback". Something like the argument to:
extern "C" void f( void (*pf)( void* ) ) ;
?
The code has been implemented by many people, so I came across
different versions of callbacks (see the code below for 3
variants).
Variant1: a normal static member function is used to redirect the callback
to a member function
Variant2: a static member function is declared, but at the definition the
extern "C" is added
Variant3: a normal C function is used.
There are no "variants". The type of the argument must
correspond to the type declared in the parameter. Language
linkage is part of the type. If the example is like the above,
the function passed to f must be `extern "C"', or the code
shouldn't compile. An `extern "C"' specification is ignored for
member functions, so the argument cannot be a member function.
Regardless.
Note that not only the linkage, but the actual number and types
of declared arguments and return values must also correspond.
Thus, for example, things like:
extern "C" void g( void*, int = 0 ) ;
extern "C" void h( void const* ) ;
extern "C" int i( void* ) ;
aren't allowed either (alghough all could be called with a
single void*, with the return value not used. The rule is very
strict; the exact types must match. (This may be
overspecificati on in the case of h, but in all of the other
cases, it's very easy to imagine exotic implementations where it
would fail.)
Note that forcing the type with a cast results in undefined
behavior, in all cases. The only thing you can do with a cast
pointer to function, other than copy it, is to cast it back the
original type. (Actually, of course, it only results in
undefined behavior if the C function calls your function without
casting the pointer back to the original type. But the
probability of that happening seems fairly high to me.)
Obviously, it works on at least two compilers (gcc 3.3.3 and a
Sun compiler, I don't know the version), as the output is for
all 3 cases "member called",
It must be a very old version of Sun CC, because current
versions of Sun CC complain. It's only a warning, since they
don't want to break code which worked with earlier versions, but
I don't think you can turn the warning off.
This is a known bug in g++. Judging from past history with g++,
it wouldn't surprise me if some future version simply declared
it an error, and refused to compile the code at all.
but I remember from a discussion that the first 2 variants are
not legal C++ code.
What does the standard say about this?
Code:
#include <iostream>
typedef void (*func)(void*);
Note that the type of func is pointer to a function with C++
language linkage. You cannot pass a pointer of type func to a C
program.
class Foo
{
public:
Foo(bool val)
{
if(val)
{
m_func = &func1;
m_arg = (void*)this;
The cast is unnecessary (and missleading---it suggests your're
playing games with the type system, which you aren't).
}
else
{
m_func = &func2;
m_arg = (void*)this;
}
}
Foo(func f) : m_func(f), m_arg((void*)th is)
{}
void call()
{
m_func(m_arg);
}
void member()
{
std::cout << "member called" << std::endl;
}
static void func1(void* arg);
static void func2(void* arg);
private:
func m_func;
void* m_arg;
};
// Variant 1: normal static member
void Foo::func1(void * arg)
{
((Foo*)arg)->member();
}
// Variant2: static member with extern "C"
extern "C" void Foo::func2(void * arg)
The `extern "C"' is ignored on member functions, so writing it
here is missleading. The case is exactly the same as func1.
{
((Foo*)arg)->member();
}
// Variant3: normal function
extern "C" void func3(void* arg)
{
((Foo*)arg)->member();
}
int main()
{
Foo foo1(true);
Foo foo2(false);
Foo foo3(func3);
The last line above shouldn't compile. You're passing a
function with "C" linkage to a function which requires a pointer
to a function with "C++" linkage. With Sun CC (5.8), I get the
following error messages:
"linkage.cc ", line 71: Warning (Anachronism): Formal argument f of
type void(*)(void*) in call to Foo::Foo(void(* )(void*)) is being
passed extern "C" void(*)(void*).
"linkage.cc ", line 71: Warning (Anachronism): Using extern "C"
void(*)(void*) to initialize void(*)(void*).
2 Warning(s) detected.
(The "Anachronis m" in the message is Sun's way of saying that
this is illegal, but because we accepted it in the past, we will
still compile the code. For the moment---perhaps not in some
future version.)
Think about it for a moment. The calling conventions for C and
for C++ are not necessarily identical---on an Intel, for
example, the most logical solution for C is different than the
most logical one for C++ (for historical reasons), and I've used
compilers for Intel where they were different.
foo1.call();
foo2.call();
foo3.call();
return 0;
}
I don't see where you have any C callback in your example, but
your code requires a diagnostic according to the standard.
Similarly, if you have a function declared like my f(), above,
the *only* function you can legally pass it is func3. Anything
else requires a diagnostic (and gives it with Sun CC).
--
James Kanze (GABI Software) email:ja******* **@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientier ter Datenverarbeitu ng
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34