www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - extern(C) and data symbols

reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
Is there a way to get access to a data symbol defined in a C library? I'm on 
Linux and I've tried
  extern (C) int foo;
or
  export int foo;
but I can't seem to get ahold of the symbol. The linker complains I'm 
changing the definition. So for now my only workaround is to define a small 
C file with

extern int foo;
int getFoo() {
 return foo;
}

and then put
  extern (C) int getFoo();
in my D code and call that whenever I need foo. Is there a better way?

thanks,
-Ben 
Jan 30 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:ctk9og$ch7$1 digitaldaemon.com...
 Is there a way to get access to a data symbol defined in a C library? I'm

 Linux and I've tried
   extern (C) int foo;
 or
   export int foo;
 but I can't seem to get ahold of the symbol. The linker complains I'm
 changing the definition. So for now my only workaround is to define a

 C file with

 extern int foo;
 int getFoo() {
  return foo;
 }

 and then put
   extern (C) int getFoo();
 in my D code and call that whenever I need foo. Is there a better way?

Sure. Declare it in a D module, then do not link in that module. That's how std.c.stdio works for the globals stdin, stdout, etc.
Jan 30 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Walter" <newshound digitalmars.com> wrote in message 
news:ctko8r$1noa$1 digitaldaemon.com...
 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message
 news:ctk9og$ch7$1 digitaldaemon.com...
 Is there a way to get access to a data symbol defined in a C library? I'm

 Linux and I've tried
   extern (C) int foo;
 or
   export int foo;
 but I can't seem to get ahold of the symbol. The linker complains I'm
 changing the definition. So for now my only workaround is to define a

 C file with

 extern int foo;
 int getFoo() {
  return foo;
 }

 and then put
   extern (C) int getFoo();
 in my D code and call that whenever I need foo. Is there a better way?

Sure. Declare it in a D module, then do not link in that module. That's how std.c.stdio works for the globals stdin, stdout, etc.

ah. that worked. I was expecting "extern" to behave like it does in C/C++ wrt data - ie it becomes a storage-class attribute instead of a linkage-type attribute. That was why I tried various combinations of export as well. As it stands I'm still going to have to have two files since my original file had function definitions that I need to include in the link command and these extern data declarations that I now need to exclude from the link command. So it looks like at least I'll avoid having a C file in there but still it seems too bad that 1) extern applied to data will confuse C/C++ programmers 2) there doesn't seem to be any way to say in the source file the D equivalent to the C/C++ meaning. It doesn't make me feel warm and fuzzy, but then there's a lot of snow outside so maybe that has something to do with it. -Ben
Jan 31 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:ctmnsr$27ae$1 digitaldaemon.com...
 ah. that worked. I was expecting "extern" to behave like it does in C/C++
 wrt data - ie it becomes a storage-class attribute instead of a

 attribute. That was why I tried various combinations of export as well. As
 it stands I'm still going to have to have two files since my original file
 had function definitions that I need to include in the link command and
 these extern data declarations that I now need to exclude from the link
 command. So it looks like at least I'll avoid having a C file in there but
 still it seems too bad that
 1) extern applied to data will confuse C/C++ programmers
 2) there doesn't seem to be any way to say in the source file the D
 equivalent to the C/C++ meaning.
 It doesn't make me feel warm and fuzzy, but then there's a lot of snow
 outside so maybe that has something to do with it.

I ran into this issue a while back when creating std.c.stdio.d, and thought of the same issues. I decided to leave it as it is because: 1) Few C libraries attempt to use global variables as part of their API. It's also considered bad form to do it. Nevertheless, it does happen. 2) A D module that only serves as an interface to C code rarely has a need to define its own code, so it rarely needs to be linked in. 3) Combining 1) and 2) means the issue comes up only rarely. Therefore, I thought it not worth the extra syntax it would need, especially since the problem has an easy workaround (I should add it to the www.digitalmars.com/techtips !) A more troublesome issue was errno, which looks like a global but isn't. Arrgh.
Feb 01 2005
next sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter wrote:

 I ran into this issue a while back when creating std.c.stdio.d, and thought
 of the same issues. I decided to leave it as it is because:
 
 1) Few C libraries attempt to use global variables as part of their API.
 It's also considered bad form to do it. Nevertheless, it does happen.
 
 2) A D module that only serves as an interface to C code rarely has a need
 to define its own code, so it rarely needs to be linked in.

One example of such an "exception" is libSDL, which uses macros in the header files. At least for SDL_main, SDL_BlitSurface and SDL_LoadBMP... Some can be covered with alias, but some require functions to replace the macros. And where there are functions, there is code. (and objects)
 /* Convenience macro -- load a surface from a file */
 #define SDL_LoadBMP(file)	SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1)

Thus: libSDL_d.a and libSDLmain_d.a, with the D wrapper code. But it's all SDL's fault. GLUT does the same thing, without any macros. Which, incidentally, also makes it (GLUT) a lot easier to use from D... And if someone says "but Windows and Linux doesn't need a custom SDLmain" one more time, they're gonna get whacked upside da head :-) --anders
Feb 01 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Anders F Björklund" <afb algonet.se> wrote in message
news:ctnkta$25r$1 digitaldaemon.com...
 Some can be covered with alias, but some require functions to replace
 the macros. And where there are functions, there is code. (and objects)

Yes. What you can do there is import a special "sdlextern.d" module into sdl.d so the user of the module never has to worry about it. Phobos does just this in std.c.linux.linux.
Feb 01 2005
parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter wrote:

Some can be covered with alias, but some require functions to replace
the macros. And where there are functions, there is code. (and objects)

Yes. What you can do there is import a special "sdlextern.d" module into sdl.d so the user of the module never has to worry about it. Phobos does just this in std.c.linux.linux.

I'm not sure that would work in this case? Since there is no original code, the only thing that exists in the C libraries symbol exports is "SDL_LoadBMP_RW" - the SDL_LoadBMP symbol is undefined (just a macro). So in order to have a function to call, I need to implement it in D. And when I do, I get object code for the function wrapper (D) code ? And this code has to be linked in somehow, I used an extra library... Hopefully, the function will be easy to inline in the release version. --anders
Feb 01 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Anders F Björklund" <afb algonet.se> wrote in message
news:ctolf7$13ui$1 digitaldaemon.com...
 Walter wrote:

Some can be covered with alias, but some require functions to replace
the macros. And where there are functions, there is code. (and objects)

Yes. What you can do there is import a special "sdlextern.d" module into sdl.d so the user of the module never has to worry about it. Phobos does just this in std.c.linux.linux.

I'm not sure that would work in this case? Since there is no original code, the only thing that exists in the C libraries symbol exports is "SDL_LoadBMP_RW" - the SDL_LoadBMP symbol is undefined (just a macro). So in order to have a function to call, I need to implement it in D. And when I do, I get object code for the function wrapper (D) code ? And this code has to be linked in somehow, I used an extra library... Hopefully, the function will be easy to inline in the release version.

Put the functions that generate code into sdl.d. Put the 'extern' declarations in sdlextern.d. In sdl.d, import sdlextern.d, but do not link in sdlextern.obj. You can see this at work in std.c.linux.linux.
Feb 01 2005
parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter wrote:

So in order to have a function to call, I need to implement it in D.
And when I do, I get object code for the function wrapper (D) code ?
And this code has to be linked in somehow, I used an extra library...

Hopefully, the function will be easy to inline in the release version.

Put the functions that generate code into sdl.d. Put the 'extern' declarations in sdlextern.d. In sdl.d, import sdlextern.d, but do not link in sdlextern.obj. You can see this at work in std.c.linux.linux.

I'm not sure you follow me here... I don't have a problem with external data symbols. I can see how that can be a problem with the surprise that "extern" works differently in D that what it does in C, but that's not the issue here in this case. The thing is that there is code generated from the SDL wrapper, in this case "sdl/video.d". And I am not including that on the compiler commandline, since I'm only compiling my own program that imports it. And when I link it, it's missing the macros-now-turned-functions:
 /usr/bin/ld: Undefined symbols:
 _SDL_BlitSurface
 __D3sdl4main19SDL_InitApplicationFAAaZi
 __init_6events9SDL_Event

While I *could* include the D module source code directly, "sdl/video.d sdl/events.d sdl/main.d", I prefer to compile those into a library, libSDL_d.a - and then include that. Either way, the D wrapper needs some extra code that C doesn't, because C uses the macro preprocessor instead of real functions. Nothing surprising about that, but it's a (tiny) hurdle to porting. But usually you can get away with just "extern(C)", const and alias. And neither of those generate any code, the problem is the macros... And like I said earlier, in this case it's due to the library design. --anders
Feb 02 2005
prev sibling next sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 2) A D module that only serves as an interface to C code rarely has a need
 to define its own code, so it rarely needs to be linked in.

In my case it was macros defined by X11's header files that are just so pervasive in C/C++ code that I translated them to D. So now I have a D file for the macros and a D file for the "pure" interface. It isn't unreasonable to keep those separate, though, so it isn't a disaster. -Ben
Feb 01 2005
prev sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
1) Few C libraries attempt to use global variables as part of their API.
It's also considered bad form to do it. Nevertheless, it does happen.

I just bumped into another situation where I had to link in a translated C header: _init_MyStruct in the data segment. In other words if the C header declares a struct and I put that definition in my D module I need to link in the module since otherwise the linker can't find any initialization values for the struct. Since that stuff isn't part of the C library it has to come from D. So I have to add the struct definitions to the same file that has all the translated macros. It's getting header to read those modules since they have function and variable declarations in one file and data structures and macros in another. Compared to the C header it seems less ideal.
Feb 01 2005
parent reply "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <Ben_member pathlink.com> wrote in message
news:ctph9f$1utc$1 digitaldaemon.com...
1) Few C libraries attempt to use global variables as part of their API.
It's also considered bad form to do it. Nevertheless, it does happen.

I just bumped into another situation where I had to link in a translated C header: _init_MyStruct in the data segment. In other words if the C header declares a struct and I put that definition in my D module I need to link

 module since otherwise the linker can't find any initialization values for

 struct. Since that stuff isn't part of the C library it has to come from

That should only be necessary if the struct has non-zero initializers.
 So I have to add the struct definitions to the same file that has all the
 translated macros. It's getting header to read those modules since they

 function and variable declarations in one file and data structures and

 another. Compared to the C header it seems less ideal.

You don't need to put the functions and variables in a separate file. Just the variables.
Feb 01 2005
parent reply Ben Hinkle <Ben_member pathlink.com> writes:
In article <ctpnrj$257m$2 digitaldaemon.com>, Walter says...
"Ben Hinkle" <Ben_member pathlink.com> wrote in message
news:ctph9f$1utc$1 digitaldaemon.com...
1) Few C libraries attempt to use global variables as part of their API.
It's also considered bad form to do it. Nevertheless, it does happen.

I just bumped into another situation where I had to link in a translated C header: _init_MyStruct in the data segment. In other words if the C header declares a struct and I put that definition in my D module I need to link

 module since otherwise the linker can't find any initialization values for

 struct. Since that stuff isn't part of the C library it has to come from

That should only be necessary if the struct has non-zero initializers.

The struct has two pointers. I noticed Anders is also missing his struct initializer __init_6events9SDL_Event. Maybe it's a Linux-only problem since I'm on Linux, too.
 So I have to add the struct definitions to the same file that has all the
 translated macros. It's getting header to read those modules since they

 function and variable declarations in one file and data structures and

 another. Compared to the C header it seems less ideal.

You don't need to put the functions and variables in a separate file. Just the variables.

You're right - it's not as bad as I thought. I now just have one file with the variables (and don't link it in) and the other file has everything else (and link it in).
Feb 02 2005
parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Ben Hinkle wrote:

 The struct has two pointers. I noticed Anders is also missing his struct
 initializer __init_6events9SDL_Event. Maybe it's a Linux-only problem since I'm
 on Linux, too.

SDL_Event is a union declaration. No idea why it generates code... --anders
Feb 02 2005