template class and static member variables

Discussion in 'C++' started by mast2as@yahoo.com, Nov 3, 2006.

  1. Guest

    Hi guys

    Here's the class I try to compile (see below). By itself when I have a
    test.cc file for example that creates an object which is an instance of
    the class SpectralProfile, it compiles fine.

    1 / Now If I move the getSpectrumAtDistance( const T &dist ) method
    definition in SpectralProfofile.cc let's say the compiler says

    core/profile.cc:199: error: `kNumBins' was not declared in this scope
    core/profile.cc:199: error: template argument 2 is invalid
    core/profile.cc:200: error: ISO C++ forbids declaration of
    `getSpectrumAtDistance' with no type
    core/profile.cc:200: error: prototype for `int
    wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)' does
    not match any in class `wSubsurface::SpectralProfile<T>'
    core/profile.hh:229: error: candidate is: wSubsurface::Vector<T,
    wSubsurface::SpectralProfile<T>::kNumBins>
    wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)
    core/profile.cc:200: error: template definition of non-template `int
    wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)'

    That's my first problem... I know a bit about instanciation of
    templated class in the cc file (to avoid linking errors). So would that
    apply to those static const variables in that case too ?

    2/

    Basically kStart kEnd ... don't really have to be templated... only the
    other member variables need to be.

    So my question is... is there a way i can make the static variables
    'untemplated' while the other are. This way when I call the function
    getSpectrumAtDistance( const T &dist ) i don't have to do:

    Vector<float, SpectralProfile<float>::kNumBins> vec =
    spectralProfile->getSpectrumAtDistance( 0.1f );

    which is a bit cumbersone... but maybe there's no better option ?

    Thank you

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    template<typename T>
    class SpectralProfile : public TProfile<T>
    {
    public:
    static const T kStart = 400.0;
    static const T kEnd = 700.0;
    static const T kBinEvery = 2.0;
    static const int kNumBins = static_cast<int>( ( kEnd - kStart ) /
    kBinEvery + 1 );

    T *m_spectrums; //!< Continuous chunk of mem that holds data

    public:
    SpectralProfile( const char * spectralProfileFile );
    ~SpectralProfile();

    Vector<T, kNumBins> getSpectrumAtDistance( const T &dist )
    {
    if ( dist <= this->m_minDistance ) {
    return Vector<T, kNumBins>( T( 0.0 ) );
    }
    else if ( dist > this->m_maxDistance ) {
    return Vector<T, kNumBins>( m_spectrums + ( this->m_numSamples -
    1 ) * kNumBins );
    }
    }
    };
     
    , Nov 3, 2006
    #1
    1. Advertising

  2. Salt_Peter Guest

    wrote:
    > Hi guys
    >
    > Here's the class I try to compile (see below). By itself when I have a
    > test.cc file for example that creates an object which is an instance of
    > the class SpectralProfile, it compiles fine.


    Thats a surprise. The member function getSpectrumAtDistance(...) is not
    static.

    >
    > 1 / Now If I move the getSpectrumAtDistance( const T &dist ) method
    > definition in SpectralProfofile.cc let's say the compiler says


    You should keep the function inline, not in its implementation file.
    Alternatively, you could specialize the template in the implementation
    file.
    Other options are in the faq.
    http://www.parashift.com/c -faq-lite/templates.html
    read section 35-15
    Any reason why you are compiling a *.cc file as opposed to a cpp?

    >
    > core/profile.cc:199: error: `kNumBins' was not declared in this scope
    > core/profile.cc:199: error: template argument 2 is invalid
    > core/profile.cc:200: error: ISO C++ forbids declaration of
    > `getSpectrumAtDistance' with no type
    > core/profile.cc:200: error: prototype for `int
    > wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)' does
    > not match any in class `wSubsurface::SpectralProfile<T>'
    > core/profile.hh:229: error: candidate is: wSubsurface::Vector<T,
    > wSubsurface::SpectralProfile<T>::kNumBins>
    > wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)
    > core/profile.cc:200: error: template definition of non-template `int
    > wSubsurface::SpectralProfile<T>::getSpectrumAtDistance(const T&)'
    >
    > That's my first problem... I know a bit about instanciation of
    > templated class in the cc file (to avoid linking errors). So would that
    > apply to those static const variables in that case too ?


    Say you had a class labelled MyStatics:

    // mystatics.h
    template<typename T>
    class MyStatics
    {
    static const T kStart;
    static const T kEnd;
    static const T kBinEvery;
    static const int kNumBins;

    T *m_spectrums; //!< Continuous chunk of mem that holds data
    };

    // mystatics.cpp
    #include "mystatics.h"

    template<typename T > const T MyStatics< T >::kStart = 400.0;
    template<typename T > const T MyStatics< T >::kEnd = 700.0;
    template<typename T > const T MyStatics< T >::kBinEvery = 2.0;
    template<typename T > const int MyStatics< T >::kNumBins =
    static_cast<int>( ( kEnd - kStart ) / kBinEvery + 1 );

    // test.cpp
    int main()
    {
    MyStatics<double> profile;
    return 0;
    }

    Personally, i'ld make the members non-static and simply use a MyStatics
    singleton.

    >
    > 2/
    >
    > Basically kStart kEnd ... don't really have to be templated... only the
    > other member variables need to be.


    and whats preventing you from doing that too?

    >
    > So my question is... is there a way i can make the static variables
    > 'untemplated' while the other are. This way when I call the function
    > getSpectrumAtDistance( const T &dist ) i don't have to do:
    >
    > Vector<float, SpectralProfile<float>::kNumBins> vec =
    > spectralProfile->getSpectrumAtDistance( 0.1f );
    >
    > which is a bit cumbersone... but maybe there's no better option ?
    >


    template< typename T >
    class MyStatics
    {
    static const double kStart;
    };

    or

    template< typename T = double >
    class MyStatics
    {
    static const T kStart;
    };

    >
    > /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    >
    > template<typename T>
    > class SpectralProfile : public TProfile<T>
    > {
    > public:
    > static const T kStart = 400.0;
    > static const T kEnd = 700.0;
    > static const T kBinEvery = 2.0;
    > static const int kNumBins = static_cast<int>( ( kEnd - kStart ) /
    > kBinEvery + 1 );
    >
    > T *m_spectrums; //!< Continuous chunk of mem that holds data
    >
    > public:
    > SpectralProfile( const char * spectralProfileFile );
    > ~SpectralProfile();
    >
    > Vector<T, kNumBins> getSpectrumAtDistance( const T &dist )


    Only static functions are allowed to access static variables.
    Declaring a member as static is rarely a warranted solution.

    > {
    > if ( dist <= this->m_minDistance ) {
    > return Vector<T, kNumBins>( T( 0.0 ) );
    > }
    > else if ( dist > this->m_maxDistance ) {
    > return Vector<T, kNumBins>( m_spectrums + ( this->m_numSamples -
    > 1 ) * kNumBins );
    > }
    > }
    > };


    Then there is the design of your class. Storing "chunks" of memory is
    the old way of doing things. Specially when you end up transfering and
    copying elements + platform padding. Why not store elements in
    sequenced containers? Why aren't you using a std::vector of Ts? That
    way you can have iterators, operators and streams do all the work for
    you.
     
    Salt_Peter, Nov 3, 2006
    #2
    1. Advertising

  3. Guest

    Thanks a lot for your answer...

    > > 1 / Now If I move the getSpectrumAtDistance( const T &dist ) method
    > > definition in SpectralProfofile.cc let's say the compiler says

    >
    > You should keep the function inline, not in its implementation file.
    > Alternatively, you could specialize the template in the implementation
    > file.
    > Other options are in the faq.
    > http://www.parashift.com/c -faq-lite/templates.html
    > read section 35-15


    okay...

    > Any reason why you are compiling a *.cc file as opposed to a cpp?


    company's policy ;-) i don't like it much but our standard Makefile
    forces the source code file to be names .hh and .cc

    > >
    > > 2/
    > >
    > > Basically kStart kEnd ... don't really have to be templated... only the
    > > other member variables need to be.

    >
    > and whats preventing you from doing that too?
    >
    > template< typename T >
    > class MyStatics
    > {
    > static const double kStart;
    > };
    >
    > or
    >
    > template< typename T = double >
    > class MyStatics
    > {
    > static const T kStart;
    > };
    >


    of course you are right ! I guess i need vacation...

    > Then there is the design of your class. Storing "chunks" of memory is
    > the old way of doing things. Specially when you end up transfering and
    > copying elements + platform padding. Why not store elements in
    > sequenced containers? Why aren't you using a std::vector of Ts? That
    > way you can have iterators, operators and streams do all the work for
    > you.


    Ah ? Okay... I thought it was better that way... instead of having a
    bunch of Vectors all other the place. I always thought in general an
    application would run faster when the data it uses are located in the
    same memory space. Will go back to Vectors then.

    I also did that because I didn't like much the allocation of memory for
    the Vectors. Since it's an array of Vectors I have to do the Vector<T,
    Depth> **m_spectrums which is fine. The problem is when I need to
    access the data of the Vectors. I have to do things like m_spectrum[ 10
    ]->w[ 12 ] (where w is the member variable of the Vector class that
    hold the vector data). Or (*m_spectrum[10])[12] which is not very
    elegant (as the Vector class has a [] operator that returns the value
    in the w array at a certain index).

    Anyway... thanks again
     
    , Nov 3, 2006
    #3
  4. Guest

    Peter

    While I am at it ?

    Is there a reason why you ask about hh/cc vs hpp/cpp ? Do you have a
    preference ?

    Okay I ended up coding the class that (kNumBins) becomes basically
    Depth in the class. Although I am forced at the end of the .cc file to
    instance the class

    template class SuperProfile<float, 3>;
    template class SuperProfile<float, 151>;
    template class SuperProfile<double, 3>;
    template class SuperProfile<double, 151>;

    I guess the float/double thing is not a problem but I am more annoyed
    by the fact that I am limited in the size of the vector (3/151) at
    compile time. Does it mean I have no way if I use this approach to
    create on the fly a SuperProfile instance of size lets say 200 ?

    template<int Depth> class SuperProfile<double, Depth>;

    // then later in the code

    SuperProfile<double, 200> aProfileVectorSize200; // That won't compile

    In function `main':test.cc:(.text+0x24e): undefined reference to
    `wSubsurface::SuperProfile<float, 200>::SuperProfile(char const*, int)'

    Any idea how to bypass that problem ?

    ////////////////////////////////////////////////////////////////////////////////////////////

    //
    //! \brief Profile class
    //

    #ifndef _PROFILE_HH_
    #define _PROFILE_HH_

    #include "vector.hh"
    #include "exception.hh"
    #include "utils.hh"

    // temp
    //#include "ciexyz31.hh"

    namespace wSubsurface {

    template<typename T, int Depth>
    class SuperProfile
    {
    public:
    T *m_distances;
    Vector<T, Depth> *m_spectrums;
    int m_numSamples;
    T m_minDistance;
    T m_maxDistance;
    public:
    SuperProfile( const char *profileFile, int numSamples );
    int binarySearch( const T &distance );
    Vector<T, Depth> getSpectrumAtDistance( const T &dist );
    };

    } // end namespace

    #endif // _PROFILE_HH_

    //
    //!\ brief Profile class, methods definitions
    //

    #include "profile.hh"
    #include "exception.hh"

    namespace wSubsurface {

    template<typename T, int Depth>
    SuperProfile<T, Depth>::SuperProfile( const char *profileFile,
    int numSamples ) :
    m_distances( NULL ),
    m_spectrums( NULL ),
    m_numSamples( numSamples ),
    m_minDistance( T( 1.0e+6 ) ),
    m_maxDistance( T( 0.0 ) )
    {
    // Read file
    ifstream ifs;

    try {
    ifs.open( profileFile );
    if ( ifs.fail() ) {
    throw( Exception( "File doesn't exist", __FILE__, __LINE__ ) );
    }
    }
    catch( const Exception &e ) {
    cerr << e.what() << endl;
    ifs.close();
    }

    int tmp; // remove that later
    ifs.read( reinterpret_cast<char*>( &tmp ), sizeof( int ) );
    ifs.read( reinterpret_cast<char*>( &tmp ), sizeof( int ) );
    ifs.read( reinterpret_cast<char*>( &tmp ), sizeof( int ) );
    ifs.read( reinterpret_cast<char*>( &tmp ), sizeof( int ) );

    m_distances = new T[ m_numSamples ];
    m_spectrums = new Vector<T, Depth>[ m_numSamples ];

    for ( int i = 0; i < m_numSamples; ++i ) {
    ifs.read( reinterpret_cast<char*>( &m_distances[ i ] ), sizeof( T )
    );
    ifs.read( reinterpret_cast<char*>( &m_spectrums[ i ].w[ 0 ] ),
    sizeof( T ) * Depth );

    if ( this->m_distances[ i ] < m_minDistance ) {
    m_minDistance = m_distances[ i ];
    }
    else if ( this->m_distances[ i ] > this->m_maxDistance ) {
    m_maxDistance = m_distances[ i ];
    }
    }

    // Close file
    ifs.close();
    cerr << "num samples: " << m_numSamples << endl;
    cerr << "min dist : " << m_minDistance << endl;
    cerr << "max dist : " << m_maxDistance << endl;
    }

    template<typename T, int Depth>
    int SuperProfile<T, Depth>::binarySearch( const T &distance )
    //
    //! \brief Returns array index of the closest distance.
    //! \param distance is the distance we try to get the closest to
    //! \return an index position in m_distances array to the closest
    distance
    //
    {
    // binary search
    int first = 0, last = m_numSamples, middle = ( first + last ) / 2;

    while( first < last ) {
    if ( distance < m_distances[ middle ] ) {
    last = middle - 1;
    }
    else if ( distance > m_distances[ middle ] ) {
    first = middle + 1;
    }
    else {
    return middle;
    }
    middle = ( first + last ) / 2;
    }

    return ( m_distances[ middle ] - distance > 0.0 ) ? middle - 1 :
    middle;
    }

    template<typename T, int Depth>
    Vector<T, Depth> SuperProfile<T, Depth>::getSpectrumAtDistance( const T
    &dist )
    {
    if ( dist <= m_minDistance ) {
    return Vector<T, Depth>( T( 0.0 ) );
    }
    else if ( dist > this->m_maxDistance ) {
    return m_spectrums[ m_numSamples - 1 ];
    }

    int idx = binarySearch( dist );

    T w = 0.5;

    return m_spectrums[ idx ] * ( 1.0 - w ) + m_spectrums[ idx + 1 ] * w;
    }

    // instance
    template class SuperProfile<float, 3>;
    template class SuperProfile<float, 151>;
    template class SuperProfile<double, 3>;
    template class SuperProfile<double, 151>;

    } // end of namespace
     
    , Nov 3, 2006
    #4
  5. Guest

    Hum I should have added that I used

    3 for a RGB color
    151 because that's the size of the data that use if use a spectral
    curve instead of RGB to describe a color.

    So I guess I am fine with those 2 templates but it would be nice to
    have a mechanisme where I can create one of the fly which has the size
    that I want for the size of the Vector in case one day i use more or
    less than 151 values for the spetral curve.

    cheers -
     
    , Nov 3, 2006
    #5
  6. Salt_Peter Guest

    wrote:
    > Thanks a lot for your answer...
    >
    > > > 1 / Now If I move the getSpectrumAtDistance( const T &dist ) method
    > > > definition in SpectralProfofile.cc let's say the compiler says

    > >
    > > You should keep the function inline, not in its implementation file.
    > > Alternatively, you could specialize the template in the implementation
    > > file.
    > > Other options are in the faq.
    > > http://www.parashift.com/c -faq-lite/templates.html
    > > read section 35-15

    >
    > okay...
    >
    > > Any reason why you are compiling a *.cc file as opposed to a cpp?

    >
    > company's policy ;-) i don't like it much but our standard Makefile
    > forces the source code file to be names .hh and .cc
    >
    > > >
    > > > 2/
    > > >
    > > > Basically kStart kEnd ... don't really have to be templated... only the
    > > > other member variables need to be.

    > >
    > > and whats preventing you from doing that too?
    > >
    > > template< typename T >
    > > class MyStatics
    > > {
    > > static const double kStart;
    > > };
    > >
    > > or
    > >
    > > template< typename T = double >
    > > class MyStatics
    > > {
    > > static const T kStart;
    > > };
    > >

    >
    > of course you are right ! I guess i need vacation...
    >
    > > Then there is the design of your class. Storing "chunks" of memory is
    > > the old way of doing things. Specially when you end up transfering and
    > > copying elements + platform padding. Why not store elements in
    > > sequenced containers? Why aren't you using a std::vector of Ts? That
    > > way you can have iterators, operators and streams do all the work for
    > > you.

    >
    > Ah ? Okay... I thought it was better that way... instead of having a
    > bunch of Vectors all other the place. I always thought in general an
    > application would run faster when the data it uses are located in the
    > same memory space. Will go back to Vectors then.


    A std::vector keeps its elements in contiguous memory. So, since
    nothing stops you from making a vector of vectors...

    >
    > I also did that because I didn't like much the allocation of memory for
    > the Vectors. Since it's an array of Vectors I have to do the Vector<T,
    > Depth> **m_spectrums which is fine. The problem is when I need to
    > access the data of the Vectors. I have to do things like m_spectrum[ 10
    > ]->w[ 12 ] (where w is the member variable of the Vector class that
    > hold the vector data). Or (*m_spectrum[10])[12] which is not very
    > elegant (as the Vector class has a [] operator that returns the value
    > in the w array at a certain index).
    >
    > Anyway... thanks again


    A std::vector is actually an array on steroids. And you have a variety
    of ways you can access the elements, including the operator[ ],
    iterators as well as a panoply of powerfull algorithms. Its also a very
    easy and safe container to use. Consider that it has the at(index)
    accessor with out_of_range bounds check. The elements are allocated
    and deallocated automatically, unless you choose to store pointers.

    I'm not doing anything fancy:

    #include <iostream>
    #include <ostream>
    #include <vector>

    struct A // toy class
    {
    int n;
    char c;
    A() : n(0), c('a') { } // def ctor
    A(size_t u, char c_) : n(static_cast<int>(u)), c(c_) { }
    // overloaded operator<<
    friend std::eek:stream&
    operator<<(std::eek:stream& os, const A& r_a)
    {
    os << "n = " << r_a.n;
    os << "\tc = " << r_a.c;
    return os;
    }
    };

    int main()
    {
    char mychar('a');
    const size_t Size(10); // const Size
    std::vector< A > va; // empty vector
    for(size_t i = 0; i < Size; ++i)
    {
    va.push_back( A(i, mychar++) ); // push 0 to 10
    }

    for(size_t i = 0; i < Size; ++i)
    {
    std::cout << va << std::endl; // <- operator [ ]
    }
    std::vector< A > vanother(1000); // makes 1000 elements instantly
    // no need to delete anything !
    }

    /*
    n = 0 c = a
    n = 1 c = b
    n = 2 c = c
    n = 3 c = d
    n = 4 c = e
    n = 5 c = f
    n = 6 c = g
    n = 7 c = h
    n = 8 c = i
    n = 9 c = j
    */

    Whats the benefit? Once you learn how to use a std::vector...
    all the other containers use nearly the same interface.
    std::list, std::deque, etc

    I'ld kindly suggest a book:
    http://www.acceleratedcpp.com/
    C++ Primer (4th Edition) Lippman, LaJoie, Moo
    http://www.josuttis.com/ (C++ Standard Library)

    All 3 are worth every penny and then some.
     
    Salt_Peter, Nov 3, 2006
    #6
    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. SaravanaKumar
    Replies:
    6
    Views:
    9,424
    Tony Morris
    Oct 19, 2004
  2. Graham Dumpleton

    Static member variables in template class.

    Graham Dumpleton, Feb 29, 2004, in forum: C++
    Replies:
    4
    Views:
    11,230
    David Baraff
    Mar 2, 2004
  3. Siemel Naran
    Replies:
    4
    Views:
    810
    Micah Cowan
    Jan 12, 2005
  4. Frederiek
    Replies:
    1
    Views:
    386
    Thomas Tutone
    Sep 14, 2006
  5. aaragon
    Replies:
    3
    Views:
    1,326
    Alf P. Steinbach
    Feb 21, 2009
Loading...

Share This Page