www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - gdc and gcc object linking issues

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
This is a bit more related to C++ than D, it has to do with wrapping.
I've got 4 files:

test.h:
class Class
{
public:
    static int statField;
};

test.cpp:
#include "test.h"
extern "C" __attribute__((dllexport))
int getStatField()
{
    return Class::statField;
}

test.d:
extern(C) int getStatField();
class Class
{
    static int statField()
    {
        return getStatField();
    }
}

main.d:
import test;
void main()
{
    int x = Class.statField();
}

This is how I compile it (XP32):
g++ -m32 -g -I. -c test.cpp -o test_cpp.o
gdc -m32 -g -I. -c test.d -o test_d.o
gdc -m32 -g -I. -o main.exe test_cpp.o test_d.o main.d -lstdc++

But I get a linker error:
test_cpp.o: In function `getStatField':
D:\dev\code\d_code\testcpplink/test.cpp:5: undefined reference to
`Class::statField'
collect2: ld returned 1 exit status

If I change the static int field to a static function (and add
parenthesis for the function call) the linking works fine, e.g.:

test.h:
class Class
{
public:
    // was: static int statField;
    static int statField() { return 1; }
};

test.cpp:
#include "test.h"
extern "C" __attribute__((dllexport))
int getStatField()
{
    // was: return Class::statField;
    return Class::statField();
}

But for variables it doesn't link. What am I doing wrong?
Jun 06 2012
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 06.06.2012 22:20, Andrej Mitrovic wrote:
 This is a bit more related to C++ than D, it has to do with wrapping.
 I've got 4 files:

 test.h:
 class Class
 {
 public:
      static int statField;
 };

Old boring C++ :) Once you have this definition due to moronic linkage model (and probably some other reasonable things) you have to have: int Class:statField; declared somewhere in you cpp files that you link together so that compiler knows where to put it (in terms of obj files).
 But for variables it doesn't link. What am I doing wrong?

Nothing, C++ is pile of dirt. :) -- Dmitry Olshansky
Jun 06 2012
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 06.06.2012 22:39, Dmitry Olshansky wrote:
 On 06.06.2012 22:20, Andrej Mitrovic wrote:
 This is a bit more related to C++ than D, it has to do with wrapping.
 I've got 4 files:

 test.h:
 class Class
 {
 public:
 static int statField;
 };

Old boring C++ :) Once you have this definition due to moronic linkage model (and probably some other reasonable things) you have to have: int Class:statField;

int Class::statField; obviously -- Dmitry Olshansky
Jun 06 2012
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 07.06.2012 23:04, Kagamin wrote:
 On Thursday, 7 June 2012 at 14:29:24 UTC, Regan Heath wrote:
 In the quoted passage above I suspect he was referring to a
 static/global variable defined in a C header, not a C++ class member
 static or otherwise.

I'm pretty sure you can define (with storage) global variable in header file both in C and C++.

Indeed you can. Kind of anti-pattern.
 But don't qualify it static in C: this will make
 it hidden symbol so you will have several instances of the variable but
 no symbol collision.

Some libraries actually count on something like this, but I can't remember offhand. Logging probably? -- Dmitry Olshansky
Jun 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/6/12, Dmitry Olshansky <dmitry.olsh gmail.com> wrote:
 Old boring C++ :)

 Once you have this definition due to moronic linkage model (and probably
 some other reasonable things) you have to have:

 int Class:statField;

int Class::statField; obviously

Thanks, that fixed it! :) Yeah C++ is a weirdo.
Jun 06 2012
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
A class declaration is simply a declaration, it doesn't allocate 
storage, so members end up being implicitly extern (or static 
inline for methods with bodies) except for instance fields, whose 
storage is allocated with the new operator. As static inlining a 
field has no sense, it becomes extern. You can declare extern 
variables in C too (try it). Extern declarations are included 
into each including module, but you can't do it with the 
variable's storage itself, so you should *define* the variable in 
a module where it will actually keep its value. If you define a 
variable in the header, it will be included in each including 
module and you'll get several instances of the variable and 
symbol collision at link time.

You can think of a class as an interface declaration which 
happens to expose some implementation details to you.
Jun 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/7/12, Kagamin <spam here.lot> wrote:
 If you define a
 variable in the header, it will be included in each including
 module and you'll get several instances of the variable and
 symbol collision at link time.

This wasn't a collision error, it was a missing symbol error. The variable is static, so it should be in the data or bss segment. You seem to be talking about instance variables but that wasn't the issue here.
Jun 07 2012
prev sibling next sibling parent "Regan Heath" <regan netmail.co.nz> writes:
On Thu, 07 Jun 2012 15:12:46 +0100, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 On 6/7/12, Kagamin <spam here.lot> wrote:
 If you define a
 variable in the header, it will be included in each including
 module and you'll get several instances of the variable and
 symbol collision at link time.

This wasn't a collision error, it was a missing symbol error. The variable is static, so it should be in the data or bss segment. You seem to be talking about instance variables but that wasn't the issue here.

I don't think he was posting a solution to the OP, he was just describing some background :) In the quoted passage above I suspect he was referring to a static/global variable defined in a C header, not a C++ class member static or otherwise. R -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Jun 07 2012
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Thursday, 7 June 2012 at 14:29:24 UTC, Regan Heath wrote:
 In the quoted passage above I suspect he was referring to a 
 static/global variable defined in a C header, not a C++ class 
 member static or otherwise.

I'm pretty sure you can define (with storage) global variable in header file both in C and C++. But don't qualify it static in C: this will make it hidden symbol so you will have several instances of the variable but no symbol collision.
Jun 07 2012
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Thursday, 7 June 2012 at 14:12:56 UTC, Andrej Mitrovic wrote:
 This wasn't a collision error, it was a missing symbol error. 
 The
 variable is static, so it should be in the data or bss segment. 
 You
 seem to be talking about instance variables but that wasn't the 
 issue
 here.

You didn't define the variable. It doesn't matter in which section a variable is placed, but which module it's defined in. In D you have declarations in .di files, but .di files alone won't work as you won't have the declared variables' storage - you'll have to link to an object compiled from the corresponding .d file because .d has definitions and that's how actual D members *definitions* are compiled and their storage allocated in sections.
Jun 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/7/12, Kagamin <spam here.lot> wrote:
 You didn't define the variable.

Aah, I see what's going on. See, I was using this in the context of wrapping existing C++ libs. Since I was writing a small test-case for wrapping a static field I made a header file with a static field declaration but no .cpp implementation files (except the wrapper). I didn't know I had to either have an initializer in the header or have a .cpp file with the definition. For actual C++ libs the definition of a static field is somewhere in an existing cpp file, so I don't have to re-define the static field when wrapping (otherwise I'd probably get collisions or multiple independent variables).
Jun 07 2012
prev sibling next sibling parent "Kagamin" <spam here.lot> writes:
On Thursday, 7 June 2012 at 23:58:21 UTC, Andrej Mitrovic wrote:
 I
 didn't know I had to either have an initializer in the header 
 or have
 a .cpp file with the definition.

Well, I'm not a C++ pro, but I won't recommend to place initializer in the header. That sounds odd. What would it mean? I guess, it will mean definition, so chances are it's not what you want. Initializers belong to definitions, and definitions belong to .cpp, that's how it usually works unless you know for sure you want something else. Bindings are just fine with declarations alone + support code.
Jun 08 2012
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/8/12, Kagamin <spam here.lot> wrote:
 Well, I'm not a C++ pro, but I won't recommend to place
 initializer in the header. That sounds odd. What would it mean? I
 guess, it will mean definition, so chances are it's not what you
 want. Initializers belong to definitions, and definitions belong
 to .cpp, that's how it usually works unless you know for sure you
 want something else. Bindings are just fine with declarations
 alone + support code.

Yeah I thought they might appear in an existing library in a header file but it seems they're in the definition file. Thanks for your help.
Jun 08 2012