Linking static const class members?

Discussion in 'C++' started by Steven T. Hatton, Sep 8, 2004.

  1. Sorry about the big code dump. I tried to get it down to the minimum
    required to demonstrate the problem. Although this is all done with GNU, I
    believe the problem I'm having may be more general. Someone on the SuSE
    programming mailing list suggested my problem is that I'm trying to execute
    a function (I assume he meant the constructor) at compile time. The same
    source code compile if I don't try to split it up into separate libraries.

    On the following source code I run:

    aclocal # sets up some kind of m4 macro magic.
    autoheader # does wonderful things with my headers
    autoconf # read the configure.ac and spit out a configure script
    automake -a -c --gnits # generate Makefile.in's from Makefile.am's
    ./configure # read Makefile.in's and generate Makefiles
    make # run make, duh!

    with the results shown at the end of the code listing. The problem seems
    to be related to my using static const class members. Other similarly
    configured programs work OK. Can someone please explain why this won't
    compile?

    ./configure.ac
    # -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.

    AC_PREREQ(2.59)
    AC_INIT(testlibs, 0.1, )
    AC_CONFIG_SRCDIR([main.cpp])
    AC_CONFIG_HEADER([config.h])

    AM_INIT_AUTOMAKE
    # Checks for programs.
    AC_PROG_CXX
    AC_PROG_CC

    # Checks for libraries.
    AC_PROG_RANLIB
    # Checks for header files.

    # Checks for typedefs, structures, and compiler characteristics.
    AC_C_CONST
    AC_C_INLINE

    # Checks for library functions.

    AC_CONFIG_FILES([Makefile
    sth/Makefile
    sth/util/Makefile])
    AC_OUTPUT



    ./Makefile.am
    INCLUDES = -I$(top_srcdir)/sth $(all_includes)
    bin_PROGRAMS = testlibs
    testlibs_SOURCES = main.cpp
    testlibs_LDFLAGS = $(all_libraries)
    testlibs_LDADD = $(top_builddir)/sth/libsth.a
    SUBDIRS = sth



    ./main.cpp
    #include "ColorTest.h"
    #include <iostream>

    int main(int argc, char* argv[]) {
    using sth::ColorTest;
    ColorTest* ct = new ColorTest(std::cout);
    }

    ./sth/Makefile.am
    INCLUDES = -I$(top_srcdir)/sth/util $(all_includes)
    SUBDIRS = util
    lib_LIBRARIES = libsth.a
    libsth_a_LIBADD = $(top_builddir)/sth/util/libutil.a
    noinst_HEADERS = ColorTest.h
    libsth_a_SOURCES = ColorTest.cpp


    ./sth/ColorTest.h
    #ifndef STHCOLORTEST_H
    #define STHCOLORTEST_H

    #include <ostream>
    namespace sth {
    class ColorTest {
    public:
    ColorTest(std::eek:stream& out);
    ~ColorTest();
    };
    };

    #endif


    ./sth/ColorTest.cpp
    #include "ColorTest.h"
    #include <iostream>

    int main(int argc, char* argv[]) {
    using sth::ColorTest;
    ColorTest* ct = new ColorTest(std::cout);
    }

    ./sth/util/Makefile.am
    INCLUDES = -I$(all_includes)
    lib_LIBRARIES = libutil.a
    noinst_HEADERS = RgbColor.h Printable_IF.h
    libutil_a_SOURCES = RgbColor.cpp


    ./sth/util/RgbColor.h
    #ifndef UTILRGBCOLOR_H
    #define UTILRGBCOLOR_H

    #include "Printable_IF.h"
    namespace sth
    {
    namespace util
    {

    /**
    @author Steven T. Hatton
    */

    class RgbColor
    : public Printable_IF
    {
    public:
    RgbColor(const unsigned char& r=255,
    const unsigned char& g=127,
    const unsigned char& b=127);

    unsigned char R() const;
    void R(const unsigned char& r);

    unsigned char G() const;
    void G(const unsigned char& g);

    unsigned char B() const;
    void B(const unsigned char& b);

    std::eek:stream& print(std::eek:stream& out) const;

    static const RgbColor RED;
    static const RgbColor GREEN;
    static const RgbColor BLUE;
    static const RgbColor BLACK;
    static const RgbColor WHITE;
    static const RgbColor MAGENTA;
    static const RgbColor CYAN;
    static const RgbColor YELLOW;
    private:
    unsigned char m_r;
    unsigned char m_g;
    unsigned char m_b;
    };

    inline unsigned char RgbColor::R() const
    {
    return m_r;
    }
    inline void RgbColor::R(const unsigned char& r)
    {
    m_r = r;
    }

    inline unsigned char RgbColor::G() const
    {
    return m_g;
    }

    inline void RgbColor::G(const unsigned char& g)
    {
    m_g = g;
    }

    inline unsigned char RgbColor::B() const
    {
    return m_b;
    }

    inline void RgbColor::B(const unsigned char& b)
    {
    m_b = b;
    }
    }
    }
    #endif

    ./sth/util/RgbColor.h
    #include "RgbColor.h"
    namespace sth
    {
    util::RgbColor::RgbColor(const unsigned char& r,
    const unsigned char& g,
    const unsigned char& b)
    : m_r(r)
    , m_g(g)
    , m_b(b)
    {}

    const util::RgbColor util::RgbColor::RED = util::RgbColor(255,0,0);
    const util::RgbColor util::RgbColor::GREEN = util::RgbColor(0,255,0);
    const util::RgbColor util::RgbColor::BLUE = util::RgbColor(0,0,255);
    const util::RgbColor util::RgbColor::BLACK = util::RgbColor(0,0,0);
    const util::RgbColor util::RgbColor::WHITE =
    util::RgbColor(255,255,255);
    const util::RgbColor util::RgbColor::MAGENTA = util::RgbColor(255,0,255);
    const util::RgbColor util::RgbColor::CYAN = util::RgbColor(0,255,255);
    const util::RgbColor util::RgbColor::YELLOW = util::RgbColor(255,255,0);

    /*!
    \fn util::RgbColor::print(std::eek:stream& out) const
    */
    std::eek:stream& util::RgbColor::print(std::eek:stream& out) const
    {
    out
    << "util::RgbColor::{r=" << this->m_r
    << ",g=" << this->m_g
    << ",b=" << this->m_b << "}\n";
    return out;
    }

    }

    ./sth/util/Printable_IF.h
    #ifndef PRINTABLE_IF_H
    #define PRINTABLE_IF_H
    #include <iostream>
    namespace sth
    {
    namespace util
    {
    class Printable_IF
    {
    public:
    Printable_IF()
    {}
    virtual ~Printable_IF()
    {}

    virtual std::eek:stream& print(std::eek:stream& out) const = 0;
    };

    inline std::eek:stream& operator<<(std::eek:stream& out, const Printable_IF&
    pif)
    {
    return pif.print(out);
    }
    }
    }
    #endif

    undefined reference to `sth::util::RgbColor::RED'
    ./sth/libsth.a(ColorTest.o)(.text+0x6f):./sth/ColorTest.cpp:9: undefined
    reference to `sth::util::RgbColor::RED'
    ./sth/libsth.a(ColorTest.o)(.text+0x77):./sth/ColorTest.cpp:9: undefined
    reference to `sth::util::RgbColor::RED'
    ./sth/libsth.a(ColorTest.o)(.text+0x81):./sth/ColorTest.cpp:9: undefined
    reference to `vtable for sth::util::RgbColor'
    ./sth/libsth.a(ColorTest.o)(.text+0x8f): In function
    `sth::ColorTest::ColorTest[in-charge](std::basic_ostream<char,
    std::char_traits<char> >&)':

    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 8, 2004
    #1
    1. Advertising

  2. Steven T. Hatton

    Mike Wahler Guest

    "Steven T. Hatton" <> wrote in message
    news:...
    > Sorry about the big code dump. I tried to get it down to the minimum
    > required to demonstrate the problem. Although this is all done with GNU,

    I
    > believe the problem I'm having may be more general. Someone on the SuSE
    > programming mailing list suggested my problem is that I'm trying to

    execute
    > a function (I assume he meant the constructor) at compile time. The same
    > source code compile if I don't try to split it up into separate libraries.
    >
    > On the following source code I run:


    We don't need all that 'make' stuff.

    [snip]
    >
    > ./main.cpp
    > #include "ColorTest.h"
    > #include <iostream>
    >
    > int main(int argc, char* argv[]) {
    > using sth::ColorTest;
    > ColorTest* ct = new ColorTest(std::cout);


    You really should 'delete' your 'ColorTest' object
    before terminating.

    > }


    [snip more 'make' stuff]

    More below.

    > ./sth/ColorTest.h
    > #ifndef STHCOLORTEST_H
    > #define STHCOLORTEST_H
    >
    > #include <ostream>
    > namespace sth {
    > class ColorTest {
    > public:
    > ColorTest(std::eek:stream& out);
    > ~ColorTest();
    > };
    > };
    >
    > #endif
    >
    >
    > ./sth/ColorTest.cpp
    > #include "ColorTest.h"
    > #include <iostream>
    >
    > int main(int argc, char* argv[]) {
    > using sth::ColorTest;
    > ColorTest* ct = new ColorTest(std::cout);
    > }


    [snip more 'make' stuff]

    More below.

    > ./sth/util/RgbColor.h
    > #ifndef UTILRGBCOLOR_H
    > #define UTILRGBCOLOR_H
    >
    > #include "Printable_IF.h"
    > namespace sth
    > {
    > namespace util
    > {
    >
    > /**
    > @author Steven T. Hatton
    > */
    >
    > class RgbColor
    > : public Printable_IF
    > {
    > public:
    > RgbColor(const unsigned char& r=255,
    > const unsigned char& g=127,
    > const unsigned char& b=127);


    I'd use symbolic names for those hard-coded numbers, in
    a central place.

    More below.

    >
    > unsigned char R() const;
    > void R(const unsigned char& r);
    >
    > unsigned char G() const;
    > void G(const unsigned char& g);
    >
    > unsigned char B() const;
    > void B(const unsigned char& b);
    >
    > std::eek:stream& print(std::eek:stream& out) const;
    >
    > static const RgbColor RED;
    > static const RgbColor GREEN;
    > static const RgbColor BLUE;
    > static const RgbColor BLACK;
    > static const RgbColor WHITE;
    > static const RgbColor MAGENTA;
    > static const RgbColor CYAN;
    > static const RgbColor YELLOW;


    Here's the real trouble.

    This is in the middle of the definition of type 'RgbColor'.
    But you're trying to define data members of this type.
    Since at this point, 'RgbColor' is not completely defined,
    You Can't Do That. You're trying to make a type contain
    itself. That makes no sense. What are the names 'RED',
    'GREEN', etc. really supposed to represent? Perhaps you
    want an enumeration here.

    More below.

    > private:
    > unsigned char m_r;
    > unsigned char m_g;
    > unsigned char m_b;
    > };
    >
    > inline unsigned char RgbColor::R() const
    > {
    > return m_r;
    > }
    > inline void RgbColor::R(const unsigned char& r)
    > {
    > m_r = r;
    > }
    >
    > inline unsigned char RgbColor::G() const
    > {
    > return m_g;
    > }
    >
    > inline void RgbColor::G(const unsigned char& g)
    > {
    > m_g = g;
    > }
    >
    > inline unsigned char RgbColor::B() const
    > {
    > return m_b;
    > }
    >
    > inline void RgbColor::B(const unsigned char& b)
    > {
    > m_b = b;
    > }
    > }
    > }
    > #endif
    >
    > ./sth/util/RgbColor.h
    > #include "RgbColor.h"
    > namespace sth
    > {
    > util::RgbColor::RgbColor(const unsigned char& r,
    > const unsigned char& g,
    > const unsigned char& b)
    > : m_r(r)
    > , m_g(g)
    > , m_b(b)
    > {}
    >
    > const util::RgbColor util::RgbColor::RED = util::RgbColor(255,0,0);


    What is this supposed to be? It's not a valid declaration or definition.
    Ask yourself:

    What type of object am I declaring here?
    What is the identifier for this object?

    [snip the rest]

    -Mike
    Mike Wahler, Sep 9, 2004
    #2
    1. Advertising

  3. Mike Wahler wrote:

    > "Steven T. Hatton" <> wrote in message
    >> On the following source code I run:

    >
    > We don't need all that 'make' stuff.


    It's the only way I know of to clearly explain what I'm trying to
    accomplish. The problem is not with the C++ code in the sense of syntax or
    semantic violations. It is valid C++

    >> int main(int argc, char* argv[]) {
    >> using sth::ColorTest;
    >> ColorTest* ct = new ColorTest(std::cout);

    >
    > You really should 'delete' your 'ColorTest' object
    > before terminating.
    >
    >> }


    Since the code was only written for the purpose of isolating the problem, it
    really isn't that important. And I really don't see the advantage in
    deleting it before termination unless there are side-effect resulting from
    the deletion which would impact the next run of the program, or some system
    resources. That isn't the case here.

    > [snip more 'make' stuff]


    > I'd use symbolic names for those hard-coded numbers, in
    > a central place.


    What's a good symbolic name that means the maximum value of a range of
    values between 0 and 255? How 'bout half that?

    >> static const RgbColor RED;
    >> static const RgbColor GREEN;
    >> static const RgbColor BLUE;
    >> static const RgbColor BLACK;
    >> static const RgbColor WHITE;
    >> static const RgbColor MAGENTA;
    >> static const RgbColor CYAN;
    >> static const RgbColor YELLOW;

    >
    > Here's the real trouble.
    >
    > This is in the middle of the definition of type 'RgbColor'.
    > But you're trying to define data members of this type.


    No, those are declarations not definitions.

    >> const util::RgbColor util::RgbColor::RED =
    >> util::RgbColor(255,0,0);

    >
    > What is this supposed to be? It's not a valid declaration or definition.
    > Ask yourself:
    >
    > What type of object am I declaring here?
    > What is the identifier for this object?


    It is of type const sth::util::RgbColor. The fully qualified identifier is
    sth::util::RgbColor::RED. This is the ONLY correct syntax for initializing
    const static members of non-integral type.

    But the code doesn't link when compiled in a separate library from the code
    using it.

    The following describes everything I did to the source to get the output
    displayed at the end. You may notice that I did make one minor adjustment
    to the code to make the output a bit more coherent.

    mkdir -p ../flatlibs/src
    cd ../flatlibs
    cp ../testlibs/src/sth/util/Printable_IF.h src
    cp ../testlibs/src/sth/util/RgbColor.h src
    cp ../testlibs/src/sth/util/RgbColor.cpp src
    cp ../testlibs/src/sth/ColorTest.h src
    cp ../testlibs/src/sth/ColorTest.cpp src
    cp ../testlibs/src/main.cpp src

    #Makefile.am
    SUBDIRS = src
    #EOF

    #src/Makefile.am
    bin_PROGRAMS = flatlibs
    flatlibs_SOURCES = \
    ColorTest.cpp\
    RgbColor.cpp\
    main.cpp

    noinst_HEADERS = \
    ColorTest.h\
    Printable_IF.h\
    RgbColor.h
    # EOF

    autoscan
    mv configure.scan configure.ac

    # configure.ac
    AC_PREREQ(2.59)
    #modify the next line
    AC_INIT(flatlibs, 0.1, )
    AC_CONFIG_SRCDIR([src/RgbColor.cpp])
    AC_CONFIG_HEADER([config.h])
    #add next line by hand
    AM_INIT_AUTOMAKE
    AC_PROG_CXX
    AC_PROG_CC
    AC_C_CONST
    AC_C_INLINE
    AC_CONFIG_FILES([Makefile
    src/Makefile])
    AC_OUTPUT
    #EOF

    aclocal
    autoheader
    autoconf
    touch INSTALL NEWS README AUTHORS ChangeLog COPYING THANKS
    automake -a -c --gnits
    ../configure
    make
    ../src/flatlibs
    util::RgbColor::{r=255,g=0,b=0}


    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 9, 2004
    #3
  4. Mike Wahler wrote:

    > "Steven T. Hatton" <> wrote in message
    > news:...
    >> Sorry about the big code dump. I tried to get it down to the minimum
    >> required to demonstrate the problem. Although this is all done with GNU,

    > I
    >> believe the problem I'm having may be more general. Someone on the SuSE
    >> programming mailing list suggested my problem is that I'm trying to

    > execute
    >> a function (I assume he meant the constructor) at compile time. The same
    >> source code compile if I don't try to split it up into separate
    >> libraries.
    >>
    >> On the following source code I run:

    >
    > We don't need all that 'make' stuff.
    >
    > [snip]


    >> const util::RgbColor util::RgbColor::RED =
    >> util::RgbColor(255,0,0);

    >
    > What is this supposed to be? It's not a valid declaration or definition.
    > Ask yourself:
    >
    > What type of object am I declaring here?
    > What is the identifier for this object?
    >
    > [snip the rest]
    >
    > -Mike


    I was able to reproduce the problem with Boost.Build. I finally figured out
    what was wrong. It was just missing a link rule in one of the
    Makefile.am's.
    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 10, 2004
    #4
    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,395
    Tony Morris
    Oct 19, 2004
  2. JFCM
    Replies:
    4
    Views:
    5,734
  3. Rakesh Sinha
    Replies:
    4
    Views:
    1,848
    Rakesh Sinha
    Jan 13, 2005
  4. Martin Rennix
    Replies:
    5
    Views:
    346
    Ron Natalie
    Apr 18, 2007
  5. Javier
    Replies:
    2
    Views:
    559
    James Kanze
    Sep 4, 2007
Loading...

Share This Page