473,785 Members | 2,419 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

static functions for callbacks


Hello all,

I'm working on a project where I came across some situations, where the GUI
library works with normal C callbacks. 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.

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", 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*);

class Foo
{
public:

Foo(bool val)
{
if(val)
{
m_func = &func1;
m_arg = (void*)this;
}
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)
{
((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);

foo1.call();
foo2.call();
foo3.call();

return 0;
}
Dec 5 '07 #1
5 2506
Michael Oswald wrote:
I'm working on a project where I came across some situations, where
the GUI library works with normal C callbacks.
That's a pretty vague statement to make in a C++ newsgroup. What
is a "normal C" callback? As soon as you bring the library to work
with a C++ linker, the "C callback" has no relevance _unless_ the
declaration of the callback has some "C" in it.
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
Usually works fine when the callback is declared as a function and
is set by passing a function pointer as an argument.
Variant2: a static member function is declared, but at the definition
the extern "C" is added
That should be no different than 'Variant1', since the Standard says
that "A C language linkage is ignored for the names of class members
and the member function type of class member functions" [dcl.link]/4
Variant3: a normal C function is used.
There can be no "normal C function" in a C++ program. There can be
a stand-alone (non-member) function whose language linkage declared
as 'extern "C"'. Is that what you mean?
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", but I remember from a discussion that the first 2
variants are not legal C++ code.
You cannot remember that because it's not true. You can remember
that you thought (or that somebody claimed) that those variants
were not legal. They are legal, however..
>
What does the standard say about this?

Code:

#include <iostream>

typedef void (*func)(void*);

class Foo
{
public:

Foo(bool val)
{
if(val)
{
m_func = &func1;
m_arg = (void*)this;
There is no need to cast. Conversion to 'void*' is implicit.
}
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();
While a C-style cast is OK, it would be better to use 'static_cast'.
}

// Variant2: static member with extern "C"
extern "C" void Foo::func2(void * arg)
{
((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);

foo1.call();
foo2.call();
foo3.call();

return 0;
}
All three cases are OK.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Dec 5 '07 #2
Victor Bazarov wrote:
>Variant2: a static member function is declared, but at the definition
the extern "C" is added

That should be no different than 'Variant1', since the Standard says
that "A C language linkage is ignored for the names of class members
and the member function type of class member functions" [dcl.link]/4
Ah, ok, that makes sense.
There can be no "normal C function" in a C++ program. There can be
a stand-alone (non-member) function whose language linkage declared
as 'extern "C"'. Is that what you mean?
Yup.
You cannot remember that because it's not true. You can remember
that you thought (or that somebody claimed) that those variants
were not legal. They are legal, however..
Ok, that's all I needed to know.
> ((Foo*)arg)->member();

While a C-style cast is OK, it would be better to use 'static_cast'.
Of course. Just to illustrate how the code I'm working with was written.

All three cases are OK.
Thanks, Victor!
Dec 5 '07 #3
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
Dec 6 '07 #4
On Dec 5, 3:40 pm, "Victor Bazarov" <v.Abaza...@com Acast.netwrote:
Michael Oswald wrote:
I'm working on a project where I came across some situations, where
the GUI library works with normal C callbacks.
That's a pretty vague statement to make in a C++ newsgroup. What
is a "normal C" callback? As soon as you bring the library to work
with a C++ linker, the "C callback" has no relevance _unless_ the
declaration of the callback has some "C" in it.
He probably means something like:
extern "C" void f( void (*)() ) ;
It's true that he didn't have anything like this in his example,
however.
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
Usually works fine when the callback is declared as a function
and is set by passing a function pointer as an argument.
Variant2: a static member function is declared, but at the definition
the extern "C" is added
That should be no different than 'Variant1', since the
Standard says that "A C language linkage is ignored for the
names of class members and the member function type of class
member functions" [dcl.link]/4
Variant3: a normal C function is used.
There can be no "normal C function" in a C++ program. There
can be a stand-alone (non-member) function whose language
linkage declared as 'extern "C"'. Is that what you mean?
You're missing a very important requirement: that the type of
the function pointer correspond to the type required by the
function it's passed to. This means that the type and number of
parameters, the type of the return, and the language linkage
must be identical. (I'm not too sure with regards to exception
specifications. But of course, they aren't relevant to a C
callback anyway.) If the type of the function pointer is not
the same as that required, then a diagnostic is required.

The language linkage IS part of the type. §7.5/1: "Two function
types with different language linkages are distinct types even
if they are otherwise identical."

An error with regards to type requires a compiler diagnostic.
We're not dealing here with undefined behavior, which the
compiler is free to defined; if a programmer passes a function
with the wrong linkage, a conforming compiler must issue a
diagnostic. (Both g++ and VC++ seem to be deficient in this
regard, but Sun CC definitly issues diagnostic, and I imagine
that most other compilers do as well. In the case of g++, it's
a known bug, and in the case of VC++, I don't know whether it's
a bug, or a case of intentionally deviating from the standard to
lock your code base in.)
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", but I remember from a
discussion that the first 2 variants are not legal C++ code.
You cannot remember that because it's not true.
He remembers correctly. The issue has been discussed here
before. Linkage is part of the type, and the types of an
argument must correspond to the types of the declared
parameters. (If the function is declared "void f( ... )", the
issue is more complex. But if the function uses va_args to
recover the argument, then the type specified within the
function must be the same as the type of the argument, or
undefined behavior results.)
You can remember that you thought (or that somebody claimed)
that those variants were not legal. They are legal, however..
If I understand correctly what he is asking, his first two
variants aren't legal, and are rejected by conforming compilers.
What does the standard say about this?
[...]
int main()
{
Foo foo1(true);
Foo foo2(false);
Foo foo3(func3);
Just for the fun of it, I added the following here:

std::cout << typeid( &Foo::func1 ).name() << std::endl ;
std::cout << typeid( &Foo::func2 ).name() << std::endl ;
std::cout << typeid( &func3 ).name() << std::endl ;

The output from Sun CC is:
void(*)(void*)
void(*)(void*)
extern "C" void(*)(void*)

Note that his func3 (declared `extern "C"') has a different
type.
All three cases are OK.
No. (Not that his example corresponds in any way to the problem
he tried to explain. What he really needs is a:

extern "C" void caller( void (*f)( void* ), void* p )
{
(*f)( p ) ;
}

Given this, he can legally call caller with func3, but not with
any of the member functions as arguments.

--
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
Dec 6 '07 #5
James Kanze wrote:
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* ) ) ;
I had a look again and saw both version, callbacks which need the C linkage
as well as free functions with C++ linkage.
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.
Ok, that's clear now.
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.)
Yes, and this makes sense to me.

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.
Sun WorkShop 6 update 1 C 5.2 2000/09/11
So it's quite old.
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.
Especially older gcc versions seem very tolerant to me.
>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.
Yes, now the difference is clear to me.
> m_arg = (void*)this;

The cast is unnecessary (and missleading---it suggests your're
playing games with the type system, which you aren't).
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).
As I already wrote in the reply to Victor, this is code like it is found in
the commercial GUI library which is used, so I cannot change it. Obviously
it runs, since the system is in use.
But it's good to know that we can run into problems when we upgrade to a
newer compiler version.
Michael

Dec 6 '07 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

76
3799
by: Nick Coghlan | last post by:
GvR has commented that he want to get rid of the lambda keyword for Python 3.0. Getting rid of lambda seems like a worthy goal, but I'd prefer to see it dropped in favour of a different syntax, rather than completely losing the ability to have anonymous functions. Anyway, I'm looking for feedback on a def-based syntax that came up in a recent c.l.p discussion:...
30
3497
by: Joost Ronkes Agerbeek | last post by:
Why is it allowed in C++ to call a static member function of an object through an instance of that object? Is it just convenience? tia, Joost Ronkes Agerbeek
1
2434
by: Icosahedron | last post by:
I know that virtual static methods have been addressed, and I'm not really looking for virtual methods, because I don't really need the run time dispatching. What I would like is to have compile time enforcement of the definition of a couple of static methods on all classes that inherit from a certain class. Unfortunately I haven't quite stumbled upon it. Even a static assert
3
1613
by: lallous | last post by:
Hello #include <iostream> #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) class Fred; typedef int (Fred::*FredMemFn)(char x, float y); class Fred {
4
2536
by: marvind | last post by:
I think I am running into the static initialization problem but I do not understand why. I am trying to parse a configuration file. To make this parser generic I register callbacks for various section keywords in the configuration file. I want to share this map of callbacks across multiple instances of the config file (for example, when I merge two files). Whenever I introduce a new section in the configuration file, I define a new class...
4
4759
by: kk_oop | last post by:
Hi. I need to write a C++ callback function and register it with a C program. I've read that this can be done if the callback function is a static method. I've also read that I should use a global function with the extern "C" prefix. I was leaning toward using the static method approach until I saw a posting that said compatibility between static C++ functions and C is not guaranteed in the respective language standards. This made me...
4
5903
by: Jimmy | last post by:
I need to use Asynchronous Socket functions in a server application and am learning from sources such as the MSDN2 (http://msdn2.microsoft.com/en-us/library/bbx2eya8.aspx). What I observed is that all callback handlers in examples are static functions. I did try non-static callback handlers; they certainly works, and they usually make my code simpler. For example, a typical tutorial will start an asynchronous connection with:
6
7684
by: smmk25 | last post by:
Before I state the problem, I just want to let the readers know, I am knew to C++\CLI and interop so please forgive any newbie questions. I have a huge C library which I want to be able to use in a .NET application and thus am looking into writing a managed C++ wrapper for in vs2005. Furthermore, this library has many callback hooks which need to be implemented by the C++ wrapper. These callback functions are declared as "extern C...
18
2495
by: Tommy Nordgren | last post by:
Given the following: def outer(arg) avar = '' def inner1(arg2) # How can I set 'avar' here ? ------------------------------------- This sig is dedicated to the advancement of Nuclear Power Tommy Nordgren tommy.nordgren@comhem.se
0
9645
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9480
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8972
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7499
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6740
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5381
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5511
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4053
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3646
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.