Jump to page: 1 2
Thread overview
'Undefined reference' linking errors
Apr 07, 2010
Joseph Wakeling
Apr 07, 2010
Ali Çehreli
Apr 07, 2010
bearophile
Apr 07, 2010
bearophile
Apr 07, 2010
Robert Clipsham
Apr 07, 2010
bearophile
Apr 08, 2010
Joseph Wakeling
Apr 08, 2010
Ali Çehreli
Apr 08, 2010
Ali Çehreli
Class-related queries [was: Re: 'Undefined reference' linking errors]
Apr 09, 2010
Joseph Wakeling
Apr 09, 2010
Joseph Wakeling
Apr 09, 2010
Joseph Wakeling
Apr 09, 2010
bearophile
Apr 09, 2010
Joseph Wakeling
Apr 09, 2010
BCS
Re: Class-related queries
Apr 09, 2010
bearophile
Re: Class-related queries [was: Re: 'Undefined reference' linking
Apr 09, 2010
bearophile
April 07, 2010
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
April 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 to

I wonder how you missed the "opCall" in there! :p

Ali
April 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
April 07, 2010
Ali Çehreli:
>  >  avg_weighted(ratings,reputation_user,reputation_object);

And I have missed this is my cleaning of the code :-)

Bye,
bearophile
April 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! :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
April 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

April 08, 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 underscore
doesn't kill).

I think it's obvious from my syntax that my background is with C; I'm not experienced with Java, C# etc.  This may explain some of the problems 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.

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
April 08, 2010
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
> experienced with Java, C# etc.  This may explain some of the problems 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
April 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
April 09, 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);
}
« First   ‹ Prev
1 2