digitalmars.D.learn - 'Undefined reference' linking errors
- Joseph Wakeling (105/105) Apr 07 2010 Hello everyone,
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (8/15) Apr 07 2010 The errors are for the missing definitions of that function. Either
- bearophile (4/5) Apr 07 2010 And I have missed this is my cleaning of the code :-)
- bearophile (4/9) Apr 07 2010 Those mangled ids are ugly. It's much better to show programmers more re...
- Robert Clipsham (7/10) Apr 07 2010 These errors are being given by the linker, and the linker doesn't know
- bearophile (65/65) Apr 07 2010 Few notes:
- Joseph Wakeling (41/53) Apr 08 2010 doesn't kill).
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (43/78) Apr 08 2010 I'm having.
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (10/12) Apr 08 2010 Didn't mean to leave out the others. These are the "reference"s in D:
- Joseph Wakeling (128/132) Apr 09 2010 I also have some C++ experience, but it seems to be confusing as much as
- Lars T. Kyllingstad (30/76) Apr 09 2010 You have stumbled upon a major difference between C++ and D. In D,
- Joseph Wakeling (16/19) Apr 09 2010 I guess I am worried about what could happen in the case of code like th...
- Joseph Wakeling (6/16) Apr 09 2010 OK, I'm impressed. :-P
- bearophile (9/15) Apr 09 2010 Also try:
- Joseph Wakeling (5/11) Apr 09 2010 To be sure I understand -- is there anything wrong with writing
- BCS (5/8) Apr 09 2010 BTW, the following might work:
- bearophile (4/6) Apr 09 2010 If the compiler is cool with that, then it's OK :-)
- Lars T. Kyllingstad (12/28) Apr 09 2010 You don't have to write 'auto' to get type inference. You just have to
- bearophile (5/16) Apr 09 2010 Thanks to you I am starting to understand, and I think it's a little str...
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
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 toI wonder how you missed the "opCall" in there! :p Ali
Apr 07 2010
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
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! :pThose 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
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
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
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 underscoredoesn'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 avariable 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 coherentIt'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
Joseph Wakeling wrote:underscore- 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 adoesn't kill). I think it's obvious from my syntax that my background is with C; I'm notI'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 whichcould in somecases 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 adataset ofuser-object ratings and user and object reputation values and helpsaggregate theratings and in the process update the reputation values. The aim was that if you just wanted a once-off analysis you could usethe class ina 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 sinceD is a newlanguage for me, I thought I would be playful with it and try andbend it aroundin some interesting ways.No harm in that. :)constructor (or- Be careful because ref arguments are tricky.The choice is deliberate here, because the arrays passed to theopCall) 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.be a bug.- There is a line like foreach (r; reputationUser) r = 1; that canI guess that I should put a 'double' in front of the r, no? In anycase, I guessthere 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.weightSum[objectID];- foreach (objectID, rating; reputationObject) rating /=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 correspondingelement ofweightSum -- is there a more intelligent way of doing this?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- I suggest to add contracts and unittests.As you might have guessed, I'm not a developer -- can you provide
Apr 08 2010
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
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
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 :-PNow 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
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
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
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
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
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
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
Joseph Wakeling wrote: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 -LarsAlso 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
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