Thread: Definition of const strings in .cpp files

  1. #1
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485

    Definition of const strings in .cpp files

    What is the best way to define const strings when there are separate header and source files?

    For example, I have a header that only declare some enums. In that same header I would like to add string representations of those enums so that I can print them easily i.e string_representation[my_enum] for debug and error printing and so on.

    If I define them in the header file, I will get a linker error for multiple definitions. If I remove the definition, then I can not define it in the source file.

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    First make sure your header file has include guards in place. Second show what you have tried.

    Jim

  3. #3
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by jimblumberg View Post
    First make sure your header file has include guards in place.
    Yes it does.

    Code:
    #ifndef SYMBOLS_H
    #define SYMBOLS_H
    
    typedef enum {  error_k,
                    plus_k,
                    minus_k,
                    mult_k,
                    div_k,
                    lparen_k,
                    rparen_k,
                    numeric_k,
                    end_k
    } symbol_t;
    
    const char *symbol_string[] = {
                    "Error",
                    "+",
                    "-",
                    "*",
                    "/",
                    "(",
                    ")",
                    "Num",
                    "End"
    };
    #endif
    This file is included in: ast, lexer and a parser all which need the symbol definitions.

    Edit:

    Here's the makefile as well

    Code:
    CC = /opt/local/bin/clang++-mp-3.2
    CFLAGS = -W -Wall -Wextra -pedantic
    
    calc: parser.o ast.o lexer.o 
            $(CC) $(CFLAGS) parser.o ast.o lexer.o 
    
    parser.o: parser.cpp
            $(CC) $(CFLAGS) -c parser.cpp
    
    ast.o: ast.cpp
            $(CC) $(CFLAGS) -c ast.cpp
    
    lexer.o: lexer.cpp
            $(CC) $(CFLAGS) -c lexer.cpp
    
    clean:
            rm *.o
    Basically, I need a way to forward declare the symbol_string so that it's recognized, and be able to define it in the .cpp file. Or if there are any other methods to do this, well I would like to know.
    Last edited by Subsonics; 03-14-2013 at 01:10 PM. Reason: Added makefile

  4. #4
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    You should probably be using the extern keyword for this variable and defining this variable (without the extern) in one of your source files.

    Your include:
    Code:
    extern const char *symbol_string[] = {
                    "Error",
                    "+",
                    "-",
                    "*",
                    "/",
                    "(",
                    ")",
                    "Num",
                    "End"
    };
    In the source file:
    Code:
     const char *symbol_string[];
    Jim

  5. #5
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by jimblumberg View Post
    You should probably be using the extern keyword for this variable and defining this variable (without the extern) in one of your source files.

    Your include:
    Code:
    extern const char *symbol_string[] = {
                    "Error",
                    "+",
                    "-",
                    "*",
                    "/",
                    "(",
                    ")",
                    "Num",
                    "End"
    };
    In the source file:
    Code:
     const char *symbol_string[];
    Jim
    I assume you mean that the first part goes in the source file and the second in the header. That gives this error:

    definition of variable with array type needs an explicit size or an initializer
    const char *symbol_string[];
    Which is the type of errors I've been seeing for a while now. It's like trying to fit a carpet that is too big to fit into a room, you move to one corner and make a perfect fit, then it pops up in the other, lol.

    Edit: swapping the extern key word to the header let me define it in the source file, however I still get the linker error.
    Last edited by Subsonics; 03-14-2013 at 01:28 PM.

  6. #6
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Yes the first part goes into the source file, minus the extern keyword. The second with the extern keyword goes into the header.

    Sorry about the mix up.



    Jim

  7. #7
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Update, it works now. I forgot to do a make clean, so I reused the old object files, from prior the change. Thanks Jim!

  8. #8
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Why not just make a function that prints a string representation of the enum value? That way you needn't muck around with extern at all, plus you can also catch use of any unrecognized enum values.

    The header file:
    Code:
    #ifndef SYMBOLS_H
    #define SYMBOLS_H
    
    // include header file that provides forward declarations of input / output streams
    #include <iosfwd>
    
    enum symbol_t
    {
        error_k,
        plus_k,
        minus_k,
        mult_k,
        div_k,
        lparen_k,
        rparen_k,
        numeric_k,
        end_k
    };
    
    
    void print_symbol_t( symbol_t enumVal, std::ostream& out );
    
    #endif
    The source file:
    Code:
    #include "Symbols.h"
    
    #include <sstream>
    #include <stdexcept>
    
    void print_symbol_t( symbol_t enumVal, std::ostream& out )
    {
        switch ( enumVal )
        {
        case error_k:
            out << "error_k";
            break;
            
        case plus_k:
            out << "plus_k";
            break;
            
        case minus_k:
            out << "minus_k";
            break;
            
        case mult_k:
            out << "mult_k";
            break;
            
        case div_k:
            out << "div_k";
            break;
            
        case lparen_k:
            out << "lparen_k";
            break;
            
        case rparen_k:
            out << "rparen_k";
            break;
            
        case numeric_k:
            out << "numeric_k";
            break;
            
        case end_k:
            out << "end_k";
            break;
            
        default:
            // Unrecognized enum value.
            {
                std::ostringstream errorMessage;
                errorMessage << "Unrecognized symbol_t enum with integer value " << enumVal << "!";
                throw std::runtime_error( errorMessage.str() );
            }
        }
    }
    EDIT: You can easily add a convenience function that returns a string, too.

    Code:
    #include <string>
    #include <sstream>
    
    std::string getStringRepresentation( symbol_t enumVal )
    {
        std::ostringstream out;
        print_symbol_t( enumVal, out );
        return out.str();
    }
    Last edited by antred; 03-15-2013 at 06:28 AM.

  9. #9
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by antred View Post
    Why not just make a function that prints a string representation of the enum value? That way you needn't muck around with extern at all, plus you can also catch use of any unrecognized enum values.
    In this case because the problem is already solved. Why add 57 lines of code on a function for something that is currently done in constant time by directly addressing the array. Unrecognized enums is not an issue here.

  10. #10
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Quote Originally Posted by Subsonics View Post
    Unrecognized enums is not an issue here.
    You say that now, but wait until some other developer adds a bunch of new values to your enum but forgets to also extend your string table.

  11. #11
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Quote Originally Posted by antred View Post
    You say that now, but wait until some other developer adds a bunch of new values to your enum but forgets to also extend your string table.
    That imaginary developer could forget to add it to you function as well. But, if I had to use a function I would only stretch this far:

    Code:
    get_string_rep(symbol_t s) {
        assert( s < sizeof(symbol_table) / sizeof(symbol_table[0])) );
        return symbol_string[s];
    }

  12. #12
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Quote Originally Posted by Subsonics View Post
    That imaginary developer could forget to add it to you function as well.
    Correct, but in my example that would cause an immediately noticeable exception, whereas in yours it would lead to undefined behavior. In the worst case, your program would appear to be working correctly while actually doing the opposite of that.

  13. #13
    Registered User
    Join Date
    Jan 2009
    Posts
    1,485
    Don't you think I'm aware of that? But as with many things it's a balance of what is reasonable in any given situation. I can make a reasonable judgement since I have insight in this situation, something you don't have.

  14. #14
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Well, then stick with your approach. I made a suggestion, nothing more.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 10-17-2012, 11:46 AM
  2. Replies: 1
    Last Post: 05-26-2010, 10:39 PM
  3. Const in Class Method Definition
    By pobri19 in forum C++ Programming
    Replies: 2
    Last Post: 09-08-2008, 03:55 AM
  4. Dynamic Libraries and definition files
    By IconTree in forum Linux Programming
    Replies: 1
    Last Post: 08-10-2005, 01:51 PM
  5. proper syntax for const array of strings?
    By cozman in forum C++ Programming
    Replies: 2
    Last Post: 09-17-2001, 01:38 PM