www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - 'Undefined reference' linking errors

reply Joseph Wakeling <joseph.wakeling gmail.com> writes:
Hello everyone,

A probably stupid but I-can't-find-the-solution-with-Google problem.

I'm trying to compile a small project I'm working on to learn D, called
'dregs'.  It's just 3 files for now (2 modules plus a little test program).
Unfortunately every time I try and compile, I get 'undefined reference' errors
for the classes/structs in the modules.

Here are the files:

// reputation.d
module dregs.reputation;

struct rating
{
        uint u;   // user ID
        uint o;   // object ID
        double r; // rating value
}

class reputation
{
        this()
        {
        }
        this(ref rating[] ratings,
             ref double[] reputation_user,
             ref double[] reputation_object)
        {
        }
}
// END

// avg.d
module dregs.avg;

public import dregs.reputation;

class avg_weighted : reputation
{
        double[] weight_sum;
        this(){}
        this(ref rating[] ratings,
             ref double[] reputation_user,
             ref double[] reputation_object)
        {
                weight_sum.length = reputation_object.length;
                foreach(r; ratings) {
                        reputation_object[r.o] += r.r;
                        weight_sum[r.o] += reputation_user[r.u];
                }
                foreach(o, r; reputation_object)
                        r /= weight_sum[o];
        }
        void opCall(ref rating[] ratings,
                    ref double[] reputation_user,
                    ref double[] reputation_object);
}

class avg_arithmetic : avg_weighted
{
        this(ref rating[] ratings,
             ref double[] reputation_user,
             ref double[] reputation_object)
        {
                foreach(r; reputation_user)
                        r = 1;
                super(ratings,reputation_user,reputation_object);
        }
        void something(ref dregs.reputation.rating[] ratings,
                       ref double[] reputation_user,
                       ref double[] reputation_object)
        {
                avg_weighted(ratings,reputation_user,reputation_object);
        }
}
// END

// test.d
import std.stdio;
import dregs.reputation;
import dregs.avg;

void main()
{
        rating[] r;
        double[] reputation_user;
        double[] reputation_object;

        reputation_user.length = 999;
        reputation_object.length = 1;

        foreach(u;0..reputation_user.length) {
                rating _r = {u,0,(u%3)};
                r ~= _r;
        }
}
// END

I'm running dmd 2.042 on Ubuntu 9.10.

   dmd -O -I../ test.d avg.d reputation.d

... produces the following errors:

------------------------------------
test.o:(.rodata+0x98): undefined reference to
`_D5dregs3avg12avg_weighted6opCallMFKAS5dregs10reputation6ratingKAdKAdZv'
test.o:(.rodata+0xf8): undefined reference to
`_D5dregs3avg12avg_weighted6opCallMFKAS5dregs10reputation6ratingKAdKAdZv'
test.o: In function
`_D5dregs3avg14avg_arithmetic9somethingMFKAS5dregs10reputation6ratingKAdKAdZv':
reputation.d:(.text._D5dregs3avg14avg_arithmetic9somethingMFKAS5dregs10reputation6ratingKAdKAdZv+0x1b):
undefined reference to
`_D5dregs3avg12avg_weighted6opCallMFKAS5dregs10reputation6ratingKAdKAdZv'
collect2: ld returned 1 exit status
--- errorlevel 1
------------------------------------

This surely is something very basic, but I couldn't find a reason for it in my
search of the archives ... :-(  Can anyone advise what I'm doing wrong?

Thanks & best wishes,

    -- Joe
Apr 07 2010
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Joseph Wakeling wrote:

         void opCall(ref rating[] ratings,
                     ref double[] reputation_user,
                     ref double[] reputation_object);
The errors are for the missing definitions of that function. Either provide a definition, or just remove that declaration. (Remove the declaration if you just want to create an object below.
  avg_weighted(ratings,reputation_user,reputation_object);
If you want to create an object of avg_weighted: auto aw = new avg_weighted(ratings,reputation_user,reputation_object);
 test.o:(.rodata+0x98): undefined reference to
 `_D5dregs3avg12avg_weighted6opCallMFKAS5dregs10reputation6ratingKAdKAdZv'
 test.o:(.rodata+0xf8): undefined reference to
I wonder how you missed the "opCall" in there! :p Ali
Apr 07 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:
  >  avg_weighted(ratings,reputation_user,reputation_object);
And I have missed this is my cleaning of the code :-) Bye, bearophile
Apr 07 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:
  > test.o:(.rodata+0x98): undefined reference to
  > `_D5dregs3avg12avg_weighted6opCallMFKAS5dregs10reputation6ratingKAdKAdZv'
  > test.o:(.rodata+0xf8): undefined reference to
 
 I wonder how you missed the "opCall" in there! :p
Those mangled ids are ugly. It's much better to show programmers more readable names in error messages. This can even become a bug report. Bye, bearophile
Apr 07 2010
parent Robert Clipsham <robert octarineparrot.com> writes:
On 07/04/10 22:19, bearophile wrote:
 Those mangled ids are ugly. It's much better to show programmers more readable
names in error messages. This can even become a bug report.
These errors are being given by the linker, and the linker doesn't know how to demangle D symbols, so it doesn't. If you make a bug it should go to the linker's bugzilla as an enhancement request... In the mean time there's several scripts out there that will demangle an input from stdin, so you can pipe the output from the linker to it and get them automatically demangled :)
 Bye,
 bearophile
Apr 07 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Few notes:
- opCall() of AvgWeighted was abstract.
- keep in mind that in D classes are CamelCase;
- variable names are written like weightSum (but once in a while a underscore
doesn't kill).
- Be careful because ref arguments are tricky.
- There is a line like foreach (r; reputationUser) r = 1; that can be a bug.
- foreach (objectID, rating; reputationObject) rating /= weightSum[objectID];
can be another bug.
- Use better attribute names in Rating struct, when you need to comment a
variable name then it's often a wrong name.
- To create structs you can most times use the syntax I've used in the main.
- In methods/functions divide your code into paragraphs;
- keep your indentations more coherent
- I suggest to add contracts and unittests.

Keeping the code tidy helps a lot avoid bugs. The following is surely not
perfect, but it's better:


struct Rating {
    uint userID, objectID;
    double rating;
}


class Reputation {
    this() {}

    this(ref Rating[] ratings,
         ref double[] reputationUser,
         ref double[] reputationObject) {}
}


class AvgWeighted : Reputation {
    double[] weightSum;

    this(ref Rating[] ratings,
         ref double[] reputationUser,
         ref double[] reputationObject) {
        weightSum.length = reputationObject.length;

        foreach (r; ratings) {
            reputationObject[r.objectID] += r.rating;
            weightSum[r.objectID] += reputationUser[r.userID];
        }

        foreach (objectID, rating; reputationObject)
            rating /= weightSum[objectID]; // useless?
    }

    void opCall(ref Rating[] ratings,
                ref double[] reputationUser,
                ref double[] reputationObject) {}
}


class AvgArithmetic : AvgWeighted {
    this(ref Rating[] ratings,
         ref double[] reputationUser,
         ref double[] reputationObject) {
        // foreach (r; reputationUser) r = 1; // bug?
        reputationUser[] = 1;
        super(ratings, reputationUser, reputationObject);
    }

    void something(ref Rating[] ratings,
                   ref double[] reputationUser,
                   ref double[] reputationObject) {
        AvgWeighted(ratings, reputationUser, reputationObject);
    }
}


void main() {
    double[] reputationUser;
    reputationUser.length = 999;

    double[] reputationObject;
    reputationObject.length = 1;

    Rating[] r;
    foreach (userID; 0 .. reputationUser.length)
        r ~= Rating(userID, 0, userID % 3);
}


Bye,
bearophile
Apr 07 2010
parent reply Joseph Wakeling <joseph.wakeling gmail.com> writes:
Thanks to everyone for the responses.

I'll respond to Bearophile's detailed comments:

 Few notes:
 - opCall() of AvgWeighted was abstract.
 - keep in mind that in D classes are CamelCase;
 - variable names are written like weightSum (but once in a while a underscore
doesn't kill). I think it's obvious from my syntax that my background is with C; I'm not having. Regarding opCall I was following the syntax described here: http://www.digitalmars.com/d/2.0/operatoroverloading.html#FunctionCall ... but clearly without understanding it properly. What I was aiming for was a bit smartarse -- to have a class which could in some cases be treated as a function. Each of these classes (later ones will be more sophisticated) is meant to be a data analysis tool which takes a dataset of user-object ratings and user and object reputation values and helps aggregate the ratings and in the process update the reputation values. The aim was that if you just wanted a once-off analysis you could use the class in a throwaway fashion -- hence the use of, avg_weighted(......); rather than avg_weighted aw(.....); The aim is that you would use the second if you were interested in employing the analysis multiple times, and that the class will have other functions that can be used for different or secondary analyses from the main one. It's maybe not the best way to approach what I want to do, but since D is a new language for me, I thought I would be playful with it and try and bend it around in some interesting ways.
 - Be careful because ref arguments are tricky.
The choice is deliberate here, because the arrays passed to the constructor (or opCall) are meant to be modified.
 - There is a line like foreach (r; reputationUser) r = 1; that can be a bug.
I guess that I should put a 'double' in front of the r, no? In any case, I guess there is a better way of setting all elements of an array equal to 1.0.
 - foreach (objectID, rating; reputationObject) rating /= weightSum[objectID];
can be another bug. ... so should be uint objectID, double rating ... ? I think it's obvious that I want each the value of each element of reputationObject to be divided by the value of the corresponding element of weightSum -- is there a more intelligent way of doing this? Reading Andrei Alexandrescu's article on Dr Dobb's gave me the impression something could be done using chain(), but I couldn't work out how (and probably misunderstood).
 - Use better attribute names in Rating struct, when you need to comment a
variable name then it's often a wrong name.
 - To create structs you can most times use the syntax I've used in the main.
 - In methods/functions divide your code into paragraphs;
 - keep your indentations more coherent
It's nice to see the stress in D on well-written code. Thanks for taking the time to clean up mine. :-)
 - I suggest to add contracts and unittests.
As you might have guessed, I'm not a developer -- can you provide more info? Thanks & best wishes, -- Joe
Apr 08 2010
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Joseph Wakeling wrote:

 - opCall() of AvgWeighted was abstract.
 - keep in mind that in D classes are CamelCase;
 - variable names are written like weightSum (but once in a while a 
underscore
 doesn't kill).

 I think it's obvious from my syntax that my background is with C; I'm not

I'm having.
 Regarding opCall I was following the syntax described here:
 http://www.digitalmars.com/d/2.0/operatoroverloading.html#FunctionCall

 ... but clearly without understanding it properly.
I have experience with C++ and still don't understand why opCall exists. :) I think I heard that opCall was needed to create struct objects before structs had constructors in D. Now structs do have constructors, which sometimes conflict with opCall. :)
 What I was aiming for was a bit smartarse -- to have a class which 
could in some
 cases be treated as a function.
I consider myself a function-happy programmer. To me, not everything is a class. :)
 Each of these classes (later ones will be more
 sophisticated) is meant to be a data analysis tool which takes a 
dataset of
 user-object ratings and user and object reputation values and helps 
aggregate the
 ratings and in the process update the reputation values.

 The aim was that if you just wanted a once-off analysis you could use 
the class in
 a throwaway fashion -- hence the use of,

    avg_weighted(......);
It could be a function that instantiates on object, that would be thrown away.
 It's maybe not the best way to approach what I want to do, but since 
D is a new
 language for me, I thought I would be playful with it and try and 
bend it around
 in some interesting ways.
No harm in that. :)
 - Be careful because ref arguments are tricky.
The choice is deliberate here, because the arrays passed to the
constructor (or
 opCall) are meant to be modified.
D has "reference types". When you pass a class object to a function "by-value", it is actually passed-by-reference. I think this is the same in Java. You can imagine the function parameter being a pointer behind the scenes. ClassType variable = new ClassType; ClassType variable2 = variable; You have a single "object" created with new, and two "variables" that refer to that object.
 - There is a line like foreach (r; reputationUser) r = 1; that can 
be a bug.
 I guess that I should put a 'double' in front of the r, no?  In any 
case, I guess
 there is a better way of setting all elements of an array equal to 1.0.
You would put 'ref' in front of the foreach variables. Otherwise they are copies in the foreach loop.
 - foreach (objectID, rating; reputationObject) rating /= 
weightSum[objectID];
 can be another bug.

 ... so should be uint objectID, double rating ... ?
Same: Should probably be 'ref rating' if you want to modify reputationObject.
 I think it's obvious that I want each the value of each element of
 reputationObject to be divided by the value of the corresponding 
element of
 weightSum -- is there a more intelligent way of doing this?
 - I suggest to add contracts and unittests.
As you might have guessed, I'm not a developer -- can you provide
more info? They are of the greater features of D. :) You can define function pre- and post-conditions and struct and class invariants. You can have unittest blocks... Great stuff! :) http://digitalmars.com/d/2.0/unittest.html http://digitalmars.com/d/2.0/dbc.html http://digitalmars.com/d/2.0/class.html#Invariant Ali
Apr 08 2010
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Ali Çehreli wrote:

 D has "reference types". When you pass a class object to a function
 "by-value", it is actually passed-by-reference.
Didn't mean to leave out the others. These are the "reference"s in D: - foreach ref parameters - ref function parameters - dynamic arrays - associative arrays - pointers I am not sure about function and delegate, and I don't know whether there is more that I am missing. Ali
Apr 08 2010
prev sibling parent reply Joseph Wakeling <joseph.wakeling gmail.com> writes:
Ali Çehreli wrote:
 I have experience with C++ and still don't understand why opCall exists.
 :) I think I heard that opCall was needed to create struct objects
 before structs had constructors in D.

 Now structs do have constructors, which sometimes conflict with opCall. :)
I also have some C++ experience, but it seems to be confusing as much as complementary with respect to D ... :-) Current source of confusion relates to declaring objects of a class whose constructor takes input -- confusion because I can write, class Foo { int x; uint y; this() { x = -1; y = 2; } } void main() { Foo f; } and have no problem, but if instead the constructor is, this(int z) { x = z; y = 2; } ... it seems like I have to write instead, auto f = new Foo(-1); ... and if I write as per C++, Foo f(-1); ... I get back a compiler error: "found 'f' when expecting ';' following 'statement'". Am I right in thinking that the 'new' syntax is necessary when the class has a constructor which takes input? This creates confusion also because in C++ one associates 'new' with dynamic allocation of memory, and it requires a consequent 'delete' statement. I know that D has GC, and I know that it also has 'delete' statements, but ... this one is 'ouch' for me :-P In my own code (cleaned up, attached following this email, and now working), I note the line, auto aw = new AvgWeighted(ratings,reputationUser,reputationObject); where if I write instead, AvgWeighted aw(ratings,reputationUser,reputationObject); ... I get the error: Error: ratings is used as a type Error: cannot have parameter of type void Error: reputationUser is used as a type Error: cannot have parameter of type void Error: reputationObject is used as a type Error: cannot have parameter of type void ... which is difficult to understand, but I presume it relates to the fact that an array is a class and not a value. Apologies if these are relatively trivial questions -- I am sure they will all become clearer once Andrei Alexandrescu's book is out ... :-) Thanks & best wishes, -- Joe ///////// My code .... struct Rating { uint u; // user ID uint o; // object ID double r; // rating value } class Reputation { this() {} this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) {} } class AvgWeighted : Reputation { double[] weightSum; this(){} this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { opCall(ratings,reputationUser,reputationObject); } void opCall(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { weightSum.length = reputationObject.length; weightSum[] = 0; reputationObject[] = 0; foreach(ref Rating r; ratings) { reputationObject[r.o] += reputationUser[r.u]*r.r; weightSum[r.o] += reputationUser[r.u]; } foreach(uint o, ref double r; reputationObject) r /= weightSum[o]; } } class AvgArithmetic : AvgWeighted { this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { opCall(ratings,reputationUser,reputationObject); } void opCall(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { reputationUser[] = 1; super.opCall(ratings,reputationUser,reputationObject); } } void main() { Rating[] ratings; double[] reputationUser; double[] reputationObject; foreach(uint u; 0..999) reputationUser ~= 0.1*(u%3); reputationObject.length = 1; reputationObject[] = 0; foreach(uint u;0..reputationUser.length) { Rating _r = {u,0,(u%3)}; writefln("%u %g",_r.u,_r.r); ratings ~= _r; } auto aw = new AvgWeighted(ratings,reputationUser,reputationObject); aw(ratings,reputationUser,reputationObject); foreach(double o; reputationObject) writefln("%g",o); }
Apr 09 2010
parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Joseph Wakeling wrote:
 I also have some C++ experience, but it seems to be confusing as much as
 complementary with respect to D ... :-)
 
 Current source of confusion relates to declaring objects of a class whose
 constructor takes input -- confusion because I can write,
 
         class Foo
         {
                 int x;
                 uint y;
 
                 this()
                 {
                         x = -1;
                         y = 2;
                 }
         }
 
         void main()
         {
                 Foo f;
         }
 
 and have no problem, but if instead the constructor is,
 
         this(int z)
         {
                 x = z;
                 y = 2;
         }
 
 ... it seems like I have to write instead,
 
         auto f = new Foo(-1);
 
 ... and if I write as per C++,
 
         Foo f(-1);
 
 ... I get back a compiler error: "found 'f' when expecting ';' following
 'statement'".  Am I right in thinking that the 'new' syntax is necessary when
the
 class has a constructor which takes input?
You have stumbled upon a major difference between C++ and D. In D, classes are reference types, and objects are allocated dynamically on the heap. This means that if you simply type Foo f; then f is null -- it is a reference to absolutely nothing -- regardless of whether Foo defines a constructor. If you try to use it for anything, you will get a null dereference error. To allocate a new object of type Foo, you use 'new': Foo f = new Foo; // No constructor Foo f = new Foo(-1); In both cases, f now points to a valid object of type Foo. The C++ syntax you are referring to does not work in D. If you want more C++-like behaviour, i.e. if you want to allocate on the stack, use a struct: struct Bar { uint x = -1; uint y = 2; this(uint z) { x = z; } } Bar b; // Use default x and y values Bar b = Bar(0); // Set x = 0 Note that you can also allocate a struct on the heap by using 'new'. In this case, it returns a pointer: Bar* b = new Bar(123);
 This creates confusion also because in C++ one associates 'new' with dynamic
 allocation of memory, and it requires a consequent 'delete' statement.  I know
 that D has GC, and I know that it also has 'delete' statements, but ... this
one
 is 'ouch' for me :-P
Now you see, 'new' is for dynamic memory allocation in D as well, it's just that for classes it is required. You normally don't need to worry about 'delete', as the GC will take care of deallocation. -Lars
Apr 09 2010
parent reply Joseph Wakeling <joseph.wakeling gmail.com> writes:
Thanks for the interesting and detailed explanation. :-)

 Now you see, 'new' is for dynamic memory allocation in D as well, it's
 just that for classes it is required.  You normally don't need to worry
 about 'delete', as the GC will take care of deallocation.
I guess I am worried about what could happen in the case of code like this in C++: for(i=0;i<10000;++i) { Foo f(i); // Do something with f ... } ... when it reappears in D as: foreach(uint i;0..10000) { auto f = new Foo(i); // Do something with f ... } Of course, it's not so terrible to have to put an explicit 'delete' statement at the end of the foreach loop if that is necessary (I am after all a C person at heart:-). It's also clear that GC will probably make such a loop more efficient as it will manage the alloc/dealloc'ing of the memory more intelligently within the system constraints. But the concern is there ...
Apr 09 2010
next sibling parent Joseph Wakeling <joseph.wakeling gmail.com> writes:
I wrote:
 I guess I am worried about what could happen in the case of code like this in
C++:

     for(i=0;i<10000;++i) {
         Foo f(i);
         // Do something with f ...
     }

 ... when it reappears in D as:

     foreach(uint i;0..10000) {
         auto f = new Foo(i);
         // Do something with f ...
     }
OK, I'm impressed. :-P I did something equivalent to the above in my 'reputation' code, and ran it through Valgrind. No difference in number of allocs/frees between a loop of 10000 and a loop of 1. I think I'm no longer worried. :-)
Apr 09 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Joseph Wakeling:
 ... when it reappears in D as:
 
     foreach(uint i;0..10000) {
         auto f = new Foo(i);
         // Do something with f ...
     }
Also try: foreach(uint i;0..10000) { scope Foo f = new Foo(i); // Do something with f ... } That sometimes doesn't require allocations. Bye, bearophile
Apr 09 2010
parent reply Joseph Wakeling <joseph.wakeling gmail.com> writes:
 Also try:

 foreach(uint i;0..10000) {
    scope Foo f = new Foo(i);
    // Do something with f ...
 }

 That sometimes doesn't require allocations.
To be sure I understand -- is there anything wrong with writing scope auto f = new Foo(i) ... or, in general, using auto to infer the class being initiated? I found myself disliking the double writing of Foo, although I guess there could be a positive side to it in ensuring that the class really is what it's meant to be.
Apr 09 2010
next sibling parent BCS <none anon.com> writes:
Hello Joseph,

 To be sure I understand -- is there anything wrong with writing
 
 scope auto f = new Foo(i)
BTW, the following might work: scope f = new Foo(i); -- ... <IXOYE><
Apr 09 2010
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Joseph Wakeling:

 is there anything wrong with writing
      scope auto f = new Foo(i)
If the compiler is cool with that, then it's OK :-) Bye, bearophile
Apr 09 2010
prev sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Joseph Wakeling wrote:
 Also try:

 foreach(uint i;0..10000) {
    scope Foo f = new Foo(i);
    // Do something with f ...
 }

 That sometimes doesn't require allocations.
To be sure I understand -- is there anything wrong with writing scope auto f = new Foo(i) ... or, in general, using auto to infer the class being initiated? I found myself disliking the double writing of Foo, although I guess there could be a positive side to it in ensuring that the class really is what it's meant to be.
You don't have to write 'auto' to get type inference. You just have to have something that signifies to the compiler that a variable is declared. All of these are valid: auto x = 123; // x is int const y = 1.23; // y is const(double) static z = "hello"; // z is string scope f = new Foo(i); // f is Foo It is a common belief that 'auto' means automatic type inference. It doesn't. 'auto' is a storage class, the same as in C: http://publib.boulder.ibm.com/infocenter/macxhelp/topic/com.ibm.vacpp6m.doc/language/ref/clrc03autdef.htm -Lars
Apr 09 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Lars T. Kyllingstad:
 You don't have to write 'auto' to get type inference.  You just have to 
 have something that signifies to the compiler that a variable is 
 declared.  All of these are valid:
 
     auto x = 123;         // x is int
     const y = 1.23;       // y is const(double)
     static z = "hello";   // z is string
     scope f = new Foo(i); // f is Foo
 
 It is a common belief that 'auto' means automatic type inference.  It 
 doesn't.  'auto' is a storage class, the same as in C:
Thanks to you I am starting to understand, and I think it's a little strange :-) I think I prefer a keyword in the language to denote "use type inferencing keyword to ask for type inference is "var": http://msdn.microsoft.com/en-us/library/bb383973.aspx ). In the end the different is very small, but you, Don, and others have had to bang this in my head a dozen times before I have started to understand it :-( Bye, bearophile
Apr 09 2010