开发者

correct idiom for character string (not std::string) constants in c++

A while ago I asked about std::string constants correct idiom for std::string constants?.

What I took away from that was not to use std::string constants but to use char string constants. So what the best idiom for that

#define FOO "foo"

const char * const FOO = "foo";

const char FOO[] = "foo";

Desirable fe开发者_StackOverflow社区atures

  • get length at compile time. 1 & 3 but not 2 (sizeof doesnt work on 2)
  • can be included in .h without linker complaining. all (I think)
  • no multiple copies in .o, in linked output. depends on compiler (probably)

So it seems like #3 is best but scott meyers says to use #2 (effective c++ item #1)

summary of answers

  1. use jolly complicated template code
  2. use #3

The template code feels like overkill. So for now I go with #3;

But I will ruminate on the template code, the macroized version makes it look OKish; but I dont like the fact that its not portable (who knows, maybe gcc will decide that its wrong too)


For the features that you want, ...

  • get length at compile time,
  • can be included in .h without linker complaining all,
  • no multiple copies in .o, in linked output,

... you can use the templated constant idiom, like

template< class Dummy >
struct Foo_
{
    static char const s[];
};

template< class Dummy >
char const Foo_<Dummy>::s[] = "Blah blah";

typedef Foo_<void> Foo;    // Now you can refer to Foo:s


#include <iostream>
using namespace std;
int main()
{
    cout << sizeof( Foo::s ) << " bytes: \"" << Foo::s << "\"\n";
}

You can wrap the generation in a macro.

However, as far as I know the only practical utility is to support char/wchar_t-agnostic code, and for that the pain may be larger than the gain.

EDIT:
MSVC versions 7.1 up through 10.0 incorrectly doesn't accept the sizeof. The following is a workaround that compiles nicely with g++ 4.4.1, Comeau Online 4.3.10.1, MSVC 7.1 and MSVC 10.0.

#include <stddef.h>

typedef ptrdiff_t   Size;

// Substitute a more general countOf
template< Size n >
struct SizedBuf { char sizer[n]; };

template< class Type, Size n >
SizedBuf< n > countOf_( Type (&)[n] ) { return n; }

#define COUNT_OF( array ) sizeof( countOf_( array ).sizer )

#define DEF_STRING( name, value )                               \
    template< class >                                           \
    struct name##_constant_                                     \
    {                                                           \
        static char const str[];                                \
        static Size const length    = COUNT_OF( value ) - 1;    \
    };                                                          \
                                                                \
    template< class Type >                                      \
    char const name##_constant_< Type >::str[] = value;         \
                                                                \
    template< class Type >                                      \
    Size const name##_constant_< Type >::length;                \
                                                                \
    typedef name##_constant_<void>  name;


DEF_STRING( a, "Argh, MSVC!" )
DEF_STRING( b, "Blah blah" )
DEF_STRING( c, "Currently there's no 'inline' for data in C++." )


#include <iostream>

template< char const* s >
void foo() { std::cout << "foo() says: " << s << std::endl; }

int main()
{
    using namespace std;

    int const x[a::length] = {};    // Showing off compile time constant.
    foo<a::str>();                  // Showing off external linkage.

    cout << a::length << " characters: \"" << a::str << "\"." << endl;
}

Cheers & hth.,


Your desired features are contradictory.

  1. Length at compile time
  2. Defined in header file
  3. Single copy across compilation units

To get (1) and (2), you need to declare the variable as static or put it in an unnamed namespace. However, this will cause a definition in each compilation unit, which goes against (3).

To get (2) and (3), you need to declare the variable as extern, but then you won't get (1).

Now, if your linker is smart, it might optimize away the multiple copies, but I'm not sure if the standard allows it...

I recommend the const char FOO[] = "foo"; syntax declared in an unnamed namespace or as static if it need to be found in a specific namespace. If the string is very large, then I go for an extern.


This is how I see it. I wouldn't use any of those as it is. First, I'm inclined by #2, but, take into account that you have to declare the variable as extern in the .h, and select some .cpp to actually store the string:

// .h
extern const char*const STR;

// .cpp
const  char*const STR = "abc";

The only drawback, not having the length at run-time, doesn't seem to me a real reason to move to other option. If you have several strings, you can always have a set of integer constants (or #defines) to specify each string's length, such as STR_LEN. If you have a lot of them, you won't write them at hand anyway, and you can then generate automatically the ..._LEN constants at the same time.


I think you took the wrong idea away from your previous question.
Strings tend to be sparse and not good for lookup, use numbers instead.

You still don't seem to get the difference between declaring in a .h and defining the storage in a .cpp thus avoiding multiple copies. Even if you had multiple copies (with different constant names) you would still have the issue you mentioned from your previous question.

has one fatal flaw. I cannot have static module level code that uses these strings since they might not have been constructed yet

The only way to avoid this is to bring the constants into the same scope with what you currently have at static module level.

Put all the related stuff in a class!


This is just Alf's answer macro'd:

#include <iostream>

#define string_constant(pName, pLiteral)                    \
        template <typename = void>                          \
        struct pName##_detail                               \
        {                                                   \
            static const char value[];                      \
        };                                                  \
                                                            \
        template <typename T>                               \
        const char pName##_detail<T>::value[] = pLiteral;   \
                                                            \
        typedef pName##_detail<> pName

string_constant(my_constant, "this is a literal");

int main()
{
    std::cout << my_constant::value << std::endl;
    std::cout << sizeof my_constant::value << std::endl;
}

codepad. Doesn't seem to work in VS2010. :/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜