7

In a header file, global constants can be declared and (pre)defined in a single line.

// constants.h
namespace Constant{
    static const unsigned int framerate = 60;
    static const char * const windowName = "Test";
    static const unsigned char * const cursorBitmap = { lots of data };
}

I like this format because it lets me keep my constants in one place, and avoids needing to declare the constant in one file and define it in another, helping with readability. When any translation unit includes constants.h however, it expands these definitions in place, per unit.

My question is, will this cause significant overhead if I include constants.h into many translation units and, for instance, cursorBitmap and other array constants are significantly large? Will my program contain 100 copies of each string and array literal if I include it into 100 units? Or will only the pointers and values be copied?

If there is overhead, is there a way I can avoid it without needing to separate declaration and definition?

(also I'm guessing the 'static' is redundant in this usage, but I like to put it there anyway)

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
  • 2
    Also of note, the C++17 draft has `inline` variables. – Simple Jan 12 '16 at 10:23
  • This does **not** avoid having to define those variables in another file. Those are declarations, and in many cases you don't have to provide a definition, but in some cases you do. For example, if you pass any of those constants to a function that takes it by reference you'll need a definition. – Pete Becker Jan 12 '16 at 15:52

3 Answers3

3

Whether string literals are duplicated in the various translation units is a Quality of Implementation issue.

The directly declared objects will be duplicated in each translation unit where this header is included. It's not much though. And in a translation unit where some constant's address isn't directly or indirectly used, it may be optimized away.

If you want to ensure only one copy of each constant, or even no copy, then you can use a class template, like this:

constants.h
#pragma once

template< class Dummy >
struct Constant_{
    static const unsigned int framerate;
    static const char * const windowName;
    static const unsigned char * const cursorBitmap;
};

template< class Dummy >
const unsigned int Constant_<Dummy>::framerate = 60;

template< class Dummy >
const char * const Constant_<Dummy::windowName = "Test";

template< class Dummy >
const unsigned char * const Constant_<Dummy>::cursorBitmap = ...;

using Constant = Constant_<void>;

But it's IMHO more work than it's worth.

A similar alternative is to use inline functions, one for each constant.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • how are TU's related to the final linked binary? "Will my program contain 100 copies of each string and array literal if I include it into 100 units? " What about the case of non-statics? – xaxxon Jan 12 '16 at 10:08
  • @xaxxon: A library and a program are two distinct kinds of things. This question is about a program. An ordinary library is a collection of (translated) translation units, which may be used singly or en masse. It's hard to think of a way to avoid duplication for that, but still it's a quality of implementation issue. – Cheers and hth. - Alf Jan 12 '16 at 10:11
  • @xaxxon: regarding "non-statics", presumably you mean external linkage. The template technique above produces external linkage constants, compatible with header-only modules. An alternative is to define the constants in an implementation file, but the OP stated she wishes to avoid that, "avoids needing to declare the constant in one file and define it in another". – Cheers and hth. - Alf Jan 12 '16 at 10:14
1

Will my program contain 100 copies of each string and array literal if I include it into 100 units? Or will only the pointers and values be copied?

The standard does not promise consolidation of string literals, so it's up to the implementation. A simple test using GCC 5.1.1 on GNU/Linux showed that the string literals are not consolidated in an unoptimized build, but are consolidated when -O or -Os is used. But that is just merging the actual char arrays. To the extent that the compiler doesn't optimize away the storage for the pointer, or for the numerical constants (if they are local to the translation unit, not ODR-used, and of POD type, they are obvious candidates for elimination under the as-if rule), however, the compiler may not be able to consolidate them easily. The standard requires them to be different objects and hence must have different addresses. The as-if rule may still allow their removal, even if you were to take their addresses, but this would generally require global program optimization aka link-time optimization, including libraries, that the implementation may not support, support only in a limited fashion, and/or only depending on compiler and linker settings. In other words, it just might happen under the right circumstances, but it is much more likely not to happen.

My own test indicates that GCC 5.1.1 does not consolidate static const unsigned int objects exposed by const ref, even with -Os -flto (optimize for size and enable link-time optimization). I would frankly be surprised if any contemporary implementation did perform this difficult and obscure optimization.

(also I'm guessing the 'static' is redundant in this usage, but I like to put it there anyway)

It's not redundant if you have multiple translation units because you would otherwise run afoul of the one definition rule (ODR). As a side note, however, static at namespace scope has been considered syntactically obsolete for a long time (consider using anonymous namespaces, introduced in C++98, instead).

(in response to Cheers and hth. - Alf)

If you want to ensure only one copy of each constant, or even no copy, then you can use a class template, like this:

No such luck. There's no guarantee in the standard about how much space is used for the templates. All the template does guarantee is that only one of the potentially many copies is used – or appears to be used, under the as-if rule. In fact, it's worse than that because at least GCC 5.1.1 in fact does not remove a redundant static const unsigned int even with -Os -flto on my system. This means with two translation units, the initializer value for the unsigned int can be found in two separate locations, even though only one of them is used (all pointers and references refer to this location only).

Arne Vogel
  • 6,346
  • 2
  • 18
  • 31
0

First, using static in namespace is deprecated since C++98:

D.2 static keyword
The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5)

Second, const implies internal linkage in C++ by itself.

Third, exact answer depends on compiler and options you use. Duplicates can be eliminated by compiler/linker, especially if LTO (Link Time Optimization) can be used.

Victor Dyachenko
  • 1,363
  • 8
  • 18