Templates, friends, operators .. what more do we need to have fun?

Discussion in 'C++' started by Micha, Sep 26, 2005.

  1. Micha

    Micha Guest

    Hello there,

    I think I've run into some classic c++ pitfall and maybe some of
    you guys can help me out.
    For my project I will need to use matrices and vectors and so I
    decided to implement them by myself. I know there are already
    tons of vector and matrix implementations, but I wanted to have
    one taylored for my needs and without debugging someones
    else code. Also is's become somewhat personal meanwhile ;-).

    The plan :
    - define a Matrix<T> and a Vector<T> class
    - make them friends to perform some operations directly - quick
    and dirty
    - define the operators processing instances of more than one type
    as friends

    After a while and learning a lot about c++ syntax subtleness I made
    the first two steps.
    Now the linker (I'm using g++ 3.3.4 without any switches refering
    templates) is nagging with complaints about not knowing the friend
    operators I've defined.

    Lets come to the details :

    I've defined both classes separated into a header and the
    implementation
    and for all my template classes I'm using a single .cpp file which
    includes
    the class template implementations to explicitly instantiate the
    specialized classes.

    Both header files (matrix and vector) include each other. They contain
    - the forward declarations of the class templates
    - the operators prototypes
    - the class template itself
    - including the references to friend classes and friend operators

    The operators are defined in the according class implementation files.

    Some more details - I will show it exemplarily :

    Vector.h
    ===================================================================
    ....

    #include "Grid2D.h"
    #include "Matrix.h"

    namespace calcapp {

    //a template method
    template <class T> void doWhateveryouwant(const T& v);

    //forward declaration of class template
    template <class T> class Vector;
    template <class T> class Matrix;

    //operator prototypes
    template <class T> Matrix<T> operator/ (const Vector<T>& v1, const
    Vector<T>& v2);
    ....

    //prototype of a test-method
    template <class T> Vector<T> doNothing(const Vector<T>& v);

    template <class T>
    class Vector : public calcapp::Grid2D<T> {
    public:
    template <class TF> friend class Matrix;

    Vector(int count_dim);
    Vector(const Vector & other);

    ....
    //tensor product
    friend Matrix<T> operator/<T> (const Vector<T>& v1, const Vector<T>&
    v2);
    //dummy
    friend Vector<T> doNothingInNS<T>(const Vector<T>& v);
    protected:
    ....
    }; //class

    } //namespace

    Vector.cpp
    ===================================================================
    #include "Vector.h"

    ....
    namespace calcapp {

    template <class T>
    Matrix<T> calcapp::eek:perator/ (const Vector<T>& v1, const Vector<T>& v2)
    {

    Matrix<T> result(v1.size_x, v2.size_x);
    ....
    return result;
    } //tensorproduct
    ....
    } //namespace


    TemplateInstances.cpp
    ===================================================================
    #include "global.h"
    #include "Vector.cpp"

    namespace calcapp {

    template class Grid2D<fptype>;

    template class Matrix<fptype>;
    template class Matrix<int32>;
    template class Matrix<int64>;

    template class Vector<fptype>;
    template class Vector<int32>;
    template class Vector<int64>;

    }

    test.cpp
    ===================================================================
    ....

    Vector<fptype) v1(3), v2(3);
    ....

    Matrix<fptype> M = v1/v2;

    > and here the compiler states :
    >../RunTest/test.cpp:310: undefined reference to `calcapp::Matrix<double> >calcapp::eek:perator/<double>(calcapp::Vector<double> const&, calcapp::Vector<double> const&)'


    ....
    ===================================================================
    So what the #!@* is wrong? That was obviously a linker error and for my
    understanding the compiler didn't generate code for operator/<fptype>.
    Correct?

    --------------------------
    The problem doesn't seem to be about operators because

    ....
    v2 = calcapp::doNothing(v1);
    ....

    would leed to an equivalent reaction.
    --------------------------
    And even

    ....
    int i = 10;
    doWhateveryouwant(i);
    ....

    won't compile (link) :
    >undefined reference to `void calcapp::doWhateveryouwant<int>(int const&)'


    So it seems it has nothing to do with classes or friends either.
    But still somehow the linker figured out that the function was defined
    in
    the namespace calcapp.
    --------------------------

    Do I have to explicitly instantiate template methods too? I've
    read nothing about that so far. How??
    Operators?

    Ok guys, what's the simple trick? ;-)


    bye, Micha
     
    Micha, Sep 26, 2005
    #1
    1. Advertising

  2. Re: Templates, friends, operators .. what more do we need to havefun?

    Micha wrote:
    > Hello there,
    >
    > I think I've run into some classic c++ pitfall and maybe some of
    > you guys can help me out.
    > For my project I will need to use matrices and vectors and so I
    > decided to implement them by myself. I know there are already
    > tons of vector and matrix implementations, but I wanted to have
    > one taylored for my needs and without debugging someones
    > else code. Also is's become somewhat personal meanwhile ;-).
    >
    > The plan :
    > - define a Matrix<T> and a Vector<T> class
    > - make them friends to perform some operations directly - quick
    > and dirty
    > - define the operators processing instances of more than one type
    > as friends
    >
    > After a while and learning a lot about c++ syntax subtleness I made
    > the first two steps.
    > Now the linker (I'm using g++ 3.3.4 without any switches refering
    > templates) is nagging with complaints about not knowing the friend
    > operators I've defined.
    >


    To be perfectly honest I haven't read all your post (bit too long for
    me) but you might care to check the FAQ. It does at least describe a
    problem with similar symptoms to yours.

    http://www.parashift.com/c -faq-lite/templates.html#faq-35.10

    john
     
    John Harrison, Sep 26, 2005
    #2
    1. Advertising

  3. Micha

    Greg Guest

    Micha wrote:
    > Hello there,
    >
    > I think I've run into some classic c++ pitfall and maybe some of
    > you guys can help me out.
    > For my project I will need to use matrices and vectors and so I
    > decided to implement them by myself. I know there are already
    > tons of vector and matrix implementations, but I wanted to have
    > one taylored for my needs and without debugging someones
    > else code. Also is's become somewhat personal meanwhile ;-).
    >
    > The plan :
    > - define a Matrix<T> and a Vector<T> class
    > - make them friends to perform some operations directly - quick
    > and dirty
    > - define the operators processing instances of more than one type
    > as friends
    >
    > After a while and learning a lot about c++ syntax subtleness I made
    > the first two steps.
    > Now the linker (I'm using g++ 3.3.4 without any switches refering
    > templates) is nagging with complaints about not knowing the friend
    > operators I've defined.
    >

    ....
    >
    > Some more details - I will show it exemplarily :
    >
    > Vector.h


    First it's a not a good idea to have a header called "vector.h" since
    there is an (obsolete) header with the same name in the system include
    paths. It seems safer to minimize confusio by renaming it with a less
    generic name.


    >
    > Vector.cpp
    > ===================================================================
    > #include "Vector.h"
    >
    > ...
    > namespace calcapp {
    >
    > template <class T>
    > Matrix<T> calcapp::eek:perator/ (const Vector<T>& v1, const Vector<T>& v2)
    > {
    >
    > Matrix<T> result(v1.size_x, v2.size_x);
    > ...
    > return result;
    > } //tensorproduct
    > ...
    > } //namespace
    >
    >
    > TemplateInstances.cpp


    It looks like this template function is being defined in a source file.
    Template definitions need to be placed in header files so the compiler
    will have seen the template defnition before it compiles source code
    that instantiates the template. With the template definition in a
    source file, it is effectively available only to other routines in this
    file which come after it.

    > #include "global.h"
    > #include "Vector.cpp"
    >
    > namespace calcapp {
    >
    > template class Grid2D<fptype>;
    >
    > template class Matrix<fptype>;
    > template class Matrix<int32>;
    > template class Matrix<int64>;
    >
    > template class Vector<fptype>;
    > template class Vector<int32>;
    > template class Vector<int64>;
    >
    > }
    >
    > test.cpp
    > ===================================================================
    > ...
    >
    > Vector<fptype) v1(3), v2(3);
    > ...
    >
    > Matrix<fptype> M = v1/v2;
    >
    > > and here the compiler states :
    > >../RunTest/test.cpp:310: undefined reference to `calcapp::Matrix<double> >calcapp::eek:perator/<double>(calcapp::Vector<double> const&, calcapp::Vector<double> const&)'

    >
    > ...
    > ===================================================================
    > So what the #!@* is wrong? That was obviously a linker error and for my
    > understanding the compiler didn't generate code for operator/<fptype>.
    > Correct?
    >


    When compiling test.cpp, the compiler did not see the definition for
    the operator/ by the time it reached line 310 that references it.
    Without a definition on hand, the compiler does not know what code to
    generate. Nor does the compiler "remember" template definitions from
    one source file to the next. Every source file that instantiates a
    template must ensure that the the template definition is included
    before its own code when it is compiled.

    The solution is to move the operator/ template and any other template
    definitions into a header file that test.cpp and other clients of these
    template classes will then include.

    Greg
     
    Greg, Sep 27, 2005
    #3
  4. Re: Templates, friends, operators .. what more do we need to havefun?

    >
    > It looks like this template function is being defined in a source file.
    > Template definitions need to be placed in header files so the compiler
    > will have seen the template defnition before it compiles source code
    > that instantiates the template. With the template definition in a
    > source file, it is effectively available only to other routines in this
    > file which come after it.
    >
    >
    >>#include "global.h"
    >>#include "Vector.cpp"
    >>


    Greg, you missed that he wrote #include "Vector.cpp". Why some people
    insist on putting template code in cpp files and then pulling all sorts
    of tricks to compensate for this is beyond me.

    john
     
    John Harrison, Sep 27, 2005
    #4
  5. Micha

    Greg Guest

    John Harrison wrote:
    > >
    > > It looks like this template function is being defined in a source file.
    > > Template definitions need to be placed in header files so the compiler
    > > will have seen the template defnition before it compiles source code
    > > that instantiates the template. With the template definition in a
    > > source file, it is effectively available only to other routines in this
    > > file which come after it.
    > >
    > >
    > >>#include "global.h"
    > >>#include "Vector.cpp"
    > >>

    >
    > Greg, you missed that he wrote #include "Vector.cpp". Why some people
    > insist on putting template code in cpp files and then pulling all sorts
    > of tricks to compensate for this is beyond me.
    >
    > john


    You're right. Though actually I did notice that #include at one point,
    but I lost track of it in that ocean of code.

    But it is worth mentioning nonetheless that source files #including
    other source files is bad form. Such nested source listings can confuse
    the debugger, the programmer, the compiler - and programming is
    confusing enough as it is, we need not invent ways to make it more so.

    Greg
     
    Greg, Sep 27, 2005
    #5
  6. Micha

    Micha Guest

    As far as I got it, the idea is to have the compiler
    generate the code for the specialized template classes
    once only in the resulting TemplateInstances.o object
    file.

    Wouldn't implementing the whole template class in the
    header file, force the compiler to instantiate the same
    MyTemplateClass<MyType> class in every object file
    generated from any .cpp file in which
    MyTemplateClass<MyType> is used?

    Btw thanks for your replies but I didn't get any further. My
    classes work just fine until I try to use those friend operators.

    The template classes which where instantiated in that .cpp
    file which includes the .cpp files of the template classes
    (together with their header files) get compiled and linked. I can
    use their member operators.

    As far as I figured it out until now, the problem is that the compiler
    won't generate code for the operators despite seeing instantiations of
    the template classes they are friends with.

    Maybe I can solve my problem if I get the following example to work.

    foo_templatestuff.h
    ====================================
    #ifndef FOO_TEMPLATESTUFF_
    #define FOO_TEMPLATESTUFF_

    template <class T>
    void doSomething(T x);
    #endif


    foo_templatestuff.cpp
    ====================================
    #include "foo_templatestuff.h"
    #include <iostream>

    template <class T>
    void doSomething(T x) {
    std::cout << x;
    }

    main.cpp
    ====================================
    #include "foo_templatestuff.h"

    int main(int argc, char *argv[]) {

    int i = 23;
    doSomething(i);

    return EXIT_SUCCESS;
    }

    Again the compiler figures out that it is
    void doSomething<int>(int) that should be called while
    processing main.cpp, but it didn't see a reason to instantiate that
    function while processing foo_templatestuff.cpp.

    Is there no other way but to define the whole function template
    in the header file instead of the prototype only?

    Ok, there is simple but dirty solution by adding a function
    void __dummy__() {
    doSomething<int>(5);
    }
    to foo_templatestuff.cpp.

    It works but I don't like it. I don't want to write some dummy function
    that touches all my operator templates for any specialization just to
    make that *#!!$ compiler generate the accordant code.

    There must be a better way ;-) ! Am I right?

    bye, Micha
     
    Micha, Sep 27, 2005
    #6
  7. Micha

    Greg Guest

    Micha wrote:
    > As far as I got it, the idea is to have the compiler
    > generate the code for the specialized template classes
    > once only in the resulting TemplateInstances.o object
    > file.
    >
    > Wouldn't implementing the whole template class in the
    > header file, force the compiler to instantiate the same
    > MyTemplateClass<MyType> class in every object file
    > generated from any .cpp file in which
    > MyTemplateClass<MyType> is used?


    Yes. In fact all of the STL container classes being templates are
    entirely implemented in header files <vector>, <list> and so on have to
    be included by any code that uses a std::vector, std::list and so
    forth. The compiler will instantiate only the methods and templates
    that a source file needs. And the linker discards duplicate template
    instantiations across files when the program is linked.

    In theory, instantiating the templates in a source file would speed up
    compile times. But the program would then have to anticipate all of the
    instantiations needed in other files. In the end, the same routines end
    up in the app anyway, so it's easier to let compiler and linker handle
    it.

    > Btw thanks for your replies but I didn't get any further. My
    > classes work just fine until I try to use those friend operators.
    >
    > The template classes which where instantiated in that .cpp
    > file which includes the .cpp files of the template classes
    > (together with their header files) get compiled and linked. I can
    > use their member operators.
    >
    > As far as I figured it out until now, the problem is that the compiler
    > won't generate code for the operators despite seeing instantiations of
    > the template classes they are friends with.


    Declaring a template class or function a friend is not enough to
    instantiate it, so this behavior is as expected.

    > Maybe I can solve my problem if I get the following example to work.
    >
    > foo_templatestuff.h
    > ====================================
    > #ifndef FOO_TEMPLATESTUFF_
    > #define FOO_TEMPLATESTUFF_
    >
    > template <class T>
    > void doSomething(T x);
    > #endif
    >
    >
    > foo_templatestuff.cpp
    > ====================================
    > #include "foo_templatestuff.h"
    > #include <iostream>
    >
    > template <class T>
    > void doSomething(T x) {
    > std::cout << x;
    > }
    >
    > main.cpp
    > ====================================
    > #include "foo_templatestuff.h"
    >
    > int main(int argc, char *argv[]) {
    >
    > int i = 23;
    > doSomething(i);
    >
    > return EXIT_SUCCESS;
    > }
    >
    > Again the compiler figures out that it is
    > void doSomething<int>(int) that should be called while
    > processing main.cpp, but it didn't see a reason to instantiate that
    > function while processing foo_templatestuff.cpp.
    >
    > Is there no other way but to define the whole function template
    > in the header file instead of the prototype only?


    Yes, just put the whole function template in the header file. Unlike a
    regular function definition, doing so will not cause link errors. Note
    also that the function template is not inlined just because it is in
    header file. The "inline" keyword would still have to be used if
    inlining is intended.

    > Ok, there is simple but dirty solution by adding a function
    > void __dummy__() {
    > doSomething<int>(5);
    > }
    > to foo_templatestuff.cpp.
    >
    > It works but I don't like it. I don't want to write some dummy function
    > that touches all my operator templates for any specialization just to
    > make that *#!!$ compiler generate the accordant code.
    >
    > There must be a better way ;-) ! Am I right?
    >
    > bye, Micha


    Depending on the compiler, it should be possible to explicitly
    instantiate the function template. Of course the definition for the
    template will be needed at the point of instantiation. Specializing the
    template should also define it.

    Moving the definitions into a header file will spare you these
    headaches. It would match the way that everybody else, include the
    standard library, does it. The compiler may end up compiling a little
    more than it needs to. But the program after it is linked is the same
    program whether you do have placed the templates in headers, or have
    succeeded after much effort in putting them somewhere else.

    Greg
     
    Greg, Sep 27, 2005
    #7
  8. Re: Templates, friends, operators .. what more do we need to havefun?

    Micha wrote:
    > As far as I got it, the idea is to have the compiler
    > generate the code for the specialized template classes
    > once only in the resulting TemplateInstances.o object
    > file.
    >
    > Wouldn't implementing the whole template class in the
    > header file, force the compiler to instantiate the same
    > MyTemplateClass<MyType> class in every object file
    > generated from any .cpp file in which
    > MyTemplateClass<MyType> is used?


    Yes and this is sometimes known as code bloat. But these days the linker
    can eliminate duplicate template code so this is not a problem.

    Put all template code in header files, it feels strange at first but it
    is the right way to do it.

    john
     
    John Harrison, Sep 28, 2005
    #8
  9. Micha

    Micha Guest

    Thank you guys, it works and I can have my sleep ;-)

    bye, Micha
     
    Micha, Sep 28, 2005
    #9
  10. Micha

    Guest


    > >>

    >
    > Greg, you missed that he wrote #include "Vector.cpp". Why some people
    > insist on putting template code in cpp files and then pulling all sorts
    > of tricks to compensate for this is beyond me.
    >
    > john


    John, my current approach when dealing with a LOT of template code is
    to to put the implementation in a .hh file. Include the .hh file in
    the .h file. How does this sound?

    I'm unsure why it's necessary to make the function definitions inline
    but I suspect I could peruse the standard to find out why.

    I too place all my template code in a .h to make life easy. This
    reminds me of a conversation I had with an individual who insists on
    using void within his function arguments for member functions that take
    no arguments. It drives me crazy and if I'm not mistaken they (see
    below) don't mean the same thing in a C++ world but I didn't have proof
    so I couldn't convice him.

    bool test::some_func(void) <- the use of void here is not the same in
    C++ correct?
    { return 1;}
     
    , Sep 29, 2005
    #10
  11. Micha

    Greg Guest

    wrote:
    > > >>

    > >
    > > Greg, you missed that he wrote #include "Vector.cpp". Why some people
    > > insist on putting template code in cpp files and then pulling all sorts
    > > of tricks to compensate for this is beyond me.
    > >
    > > john

    >
    > John, my current approach when dealing with a LOT of template code is
    > to to put the implementation in a .hh file. Include the .hh file in
    > the .h file. How does this sound?
    >
    > I'm unsure why it's necessary to make the function definitions inline
    > but I suspect I could peruse the standard to find out why.


    Placing the function templates in a header file does not implicitly
    "inline" them. The functions still need to be declared with the
    "inline" keyword for the compiler to treat them as inline functions.
    The reason for putting the templates in the header file is to ensure
    that the compiler will have seen the template definition should it need
    to instantiate it when compiling a source file.

    > I too place all my template code in a .h to make life easy. This
    > reminds me of a conversation I had with an individual who insists on
    > using void within his function arguments for member functions that take
    > no arguments. It drives me crazy and if I'm not mistaken they (see
    > below) don't mean the same thing in a C++ world but I didn't have proof
    > so I couldn't convice him.
    >
    > bool test::some_func(void) <- the use of void here is not the same in
    > C++ correct?
    > { return 1;}


    I think you have specifics reversed - that is, the declaration without
    "void":

    int some_func();

    is not the same in C as it is in C++. In C++ some_func accepts no
    parameters. In C, some_func may accept an int parameter (since the
    parameter list has been omitted, an int parameter is assumed).

    To declare a a function that accepts no parameters in C it is therefore
    necessary to use the "void" parameter list in order to distinguish it
    from the declaration without a paramter list - and for which an int
    parameter is assumed. C++ never needs a void parameter list, since the
    parameter list is always present in a function's declaration; if the
    parameter list is empty, then the function accepts no parameters.

    So although the void is unnecessary and even annoying in C++, it is not
    actually changing the meaning of the declaration.

    Greg
     
    Greg, Sep 29, 2005
    #11
  12. Micha

    Guest

    Greg wrote:
    > wrote:
    > > > >>
    > > >
    > > > Greg, you missed that he wrote #include "Vector.cpp". Why some people
    > > > insist on putting template code in cpp files and then pulling all sorts
    > > > of tricks to compensate for this is beyond me.
    > > >
    > > > john

    > >
    > > John, my current approach when dealing with a LOT of template code is
    > > to to put the implementation in a .hh file. Include the .hh file in
    > > the .h file. How does this sound?
    > >
    > > I'm unsure why it's necessary to make the function definitions inline
    > > but I suspect I could peruse the standard to find out why.

    >
    > Placing the function templates in a header file does not implicitly
    > "inline" them. The functions still need to be declared with the
    > "inline" keyword for the compiler to treat them as inline functions.
    > The reason for putting the templates in the header file is to ensure
    > that the compiler will have seen the template definition should it need
    > to instantiate it when compiling a source file.
    >
    > > I too place all my template code in a .h to make life easy. This
    > > reminds me of a conversation I had with an individual who insists on
    > > using void within his function arguments for member functions that take
    > > no arguments. It drives me crazy and if I'm not mistaken they (see
    > > below) don't mean the same thing in a C++ world but I didn't have proof
    > > so I couldn't convice him.
    > >
    > > bool test::some_func(void) <- the use of void here is not the same in
    > > C++ correct?
    > > { return 1;}

    >
    > I think you have specifics reversed - that is, the declaration without
    > "void":
    >
    > int some_func();
    >
    > is not the same in C as it is in C++. In C++ some_func accepts no
    > parameters. In C, some_func may accept an int parameter (since the
    > parameter list has been omitted, an int parameter is assumed).
    >
    > To declare a a function that accepts no parameters in C it is therefore
    > necessary to use the "void" parameter list in order to distinguish it
    > from the declaration without a paramter list - and for which an int
    > parameter is assumed. C++ never needs a void parameter list, since the
    > parameter list is always present in a function's declaration; if the
    > parameter list is empty, then the function accepts no parameters.
    >
    > So although the void is unnecessary and even annoying in C++, it is not
    > actually changing the meaning of the declaration.
    >
    > Greg


    Appreaciate it Greg!! You're right I had my specifics backwords. I'll
    get over my annoyance - I suppose :)
     
    , Sep 29, 2005
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andy Fish
    Replies:
    65
    Views:
    1,774
    Mabden
    May 18, 2004
  2. Replies:
    2
    Views:
    604
  3. dolphin
    Replies:
    4
    Views:
    324
    Jorgen Grahn
    Aug 25, 2007
  4. Replies:
    0
    Views:
    662
  5. er
    Replies:
    2
    Views:
    509
Loading...

Share This Page