2

Update,

I tried removing the static modifier, and I tried putting them in a namespace (as well as both of those), and none worked.


Hi,

I have a header file with common constants like names and stuff that are automatically included in each project (an example follows). The thing is that they are included in the compiled binary (EXE) whether they are used (referenced) or not. If I use DEFINEs instead, then naturally they are not included if they are not used, but of course consts are better than defines so… I tried Googling it, but the closest thing I could find was a question right here on SO that did not quite help. Matters of i18n aside, how can I keep the ones that are not used out of the binary, while still keeping it easy to use like this?

Thanks.


//COMMON.H:
  static const CString s_Company _T("Acme inc.");
  //etc. others
  static const CString s_Digits  _T("0123456789");

//TEST.CPP:
  #include common.h
  int main() {
    AfxMessageBox(s_Company);
  }

  //s_Company should be in the final EXE, but s_Digits should not be, but is
Community
  • 1
  • 1
Synetech
  • 9,643
  • 9
  • 64
  • 96
  • Could you put them in a shared library? – JRL Apr 17 '10 at 15:16
  • out of curiosity, why do you care to enforce that these symbols are stripped? – Stephen Apr 17 '10 at 15:17
  • @Stephan, There are more than just the two I put in the example, so they add to the binary’s size, and they create a lot of extra chaff in the binary (eg running strings on it gives extra stuff that is not helpful). – Synetech Apr 17 '10 at 15:44

4 Answers4

2

The reason they're not stripped from the binary is because they are used: CString is not a POD type, so when you create instances of them at global scope, the compiler has to generate code to call their constructors and destructors.

If you want unused symbols to be stripped, just replace the CStrings with a POD type such as const TCHAR*:

static const TCHAR *s_Company = _T("Acme inc.");
static const TCHAR *s_Digits  = _T("0123456789");

Then, the unused constants will be stripped from your binary automatically by the compiler. However, one important thing to keep in mind is that if your strings are used in multiple files, then your binary will have multiple copies of those strings in it, one copy for each translation unit that uses the string. Not even gcc's -fmerge-constants option seems to fix this. If you don't want this to happen, you'll need to use extern declarations in your header files and then put the string constants' values in source files (usually in one file, but that's not required). This also allows you to change the constants without needing to recompile every file that uses them.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • @Adam, That makes sense. Unfortunately I use CString so that I can combine some, eg, `CString lcletters="abc…"; const CString ucletters="ABC…"; const CString letters=lcletters+ucletters;` (again, i18n aside). Using TCHAR* won’t work since the compilers interprets it as adding pointers. Also, some of the common constants are arrays of structs/classes (eg `const SFType s_SpecialFolders[]={{0x1234, _T("{20D04FE0-3AEA-1069-A2D8-08002B30309D}"), _T("My Computer")}};`). Is there no way for the compiler to not include a variable at all if it is unused (ie not run the constructor)? – Synetech Apr 17 '10 at 16:15
  • Oh, and if not, then is there an alternative (without resorting to DLLs)? – Synetech Apr 17 '10 at 16:16
  • No. If the variable has a constructor, the compiler *cannot delete it*. So, your only options are to use a POD type without a constructor/destructor, or accept that you'll have all of your strings many times over in your binary. Alternatively, just put `extern` declarations in your header and put the string definitions in a separate source file, so that each string only exists once in your binary. – Adam Rosenfield Apr 17 '10 at 20:47
  • Okay, I’ll put the strings in a source file and extern the header (it would make this one finally consistent with every other one of my other libraries anyway—library here meaning header/source pairs, not compiled LIBs). Oh, and for the record, the strings appear only once in the binary. – Synetech Apr 18 '10 at 00:29
1

If you don't need the values as literals, you can declare them in the header file as:

extern const CString s_Company;

Put each one of them in its own source file that defines it as:

const CString s_Company _T("Acme inc.");

Then you can only link in the constants that you need; the linker will tell you in its error messages if you're missing any! (There are also ways to tell the compiler to not make those symbols public in the built library – assuming you're building a library at all – but they're not standard or portable.)

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • 1
    If you combine those objects into a static library and then link against the archive instead of the objects, the linker will only use the objects that satisfy an undefined reference. – R Samuel Klatchko Apr 17 '10 at 15:28
  • @Donal, There’s more than just the two I gave in the example, so it would be a horrible mess to have each in a separate file, especially since there are some that are related to each other. @R.S. Klatchko Library as in DLL? I’m hoping to avoid DLLs because they are not as easy to use as a simple #include (don’t have to give the header file its own project and separately re-compile it for every little change). – Synetech Apr 17 '10 at 15:42
  • @Synetechinc. - not a DLL, a **static** library. Agreed, not as easy as just using a header file, but not much harder. Since you are using MS specific code, I'm guessing you use Visual Studio; you can have project dependencies so that the static library will get recompiled as necessary. – R Samuel Klatchko Apr 17 '10 at 16:05
  • Oh right, you mean a LIB file right? Hmm, I actually don’t use libraries like that on my own (a few of my projects link against some 3rd party libs). I’m not clear on how that would help though since if the linker is the one to leave out the unused stuff, then why can’t it do so for the project itself? – Synetech Apr 17 '10 at 16:18
  • @Synetech: Ultimately, you've got to not put the symbol definition in the program. You either do that by `#ifdef`fery or by not linking the symbol. If you won't go for either solution, you're *stuck*. – Donal Fellows Apr 17 '10 at 16:57
  • The linking method sounds find, but how can I get the linker to not include variables that are never referenced in the program itself? As Adam said, the constructor is run, so technically it is referenced, but it is still unused in the program itself, so if it does not fiddle with resources (eg using files, accessing a database, etc.), then it is like it does not exist in that program. Is there some sort of flag that the linker can use to understand when they are truly unused and leave them out? – Synetech Apr 17 '10 at 17:29
  • If you make a *static* library out of these lots of little object files (each with a single variable or group of variables that are always used together) then the linker will only link in the symbols that are required. (Well, that's what happens with Unix linkers; I suspect that Windows linkers are the same but I don't actually know...) – Donal Fellows Apr 17 '10 at 17:57
  • Under either OS, would it still happen if they are classes that have constructors? Adam suggests that only PODTs are removed which unfortunately makes sense. I’m going to separate them into a source/header pair as you guys suggested. Whether or not it helps this particular issue, it will at the very least make this unit consistent with my other ones. :) – Synetech Apr 18 '10 at 00:34
  • I know more about C than C++, but I *think* that the removal will work even with constructors in the picture. That's because each object file contains an internal symbol called at startup (exactly for constructors) by code issued during the link phase, which is the earliest that anything knows just what needs to be constructed. (There's a corresponding mechanism for destructors.) – Donal Fellows Apr 18 '10 at 06:32
0

If there are clear rules defined that make it clear when you want to use what, then you can use preprocessor defines like this:

#if defined CONFIG_COMPANY
  static const CString s_Company _T("Acme inc.");
#elif defined CONFIG_DIGITS
  static const CString s_Digits  _T("0123456789");
#endif

You can then define or not define either of these values in a separate configuration header.

By the way, if this file is meant to be included in many source files, then you should probably refrain from declaring these constants as static. They have internal linkage and you'll get a separate copy of them in all of your translation units.

Zoli
  • 1,137
  • 7
  • 12
  • Nope, there is no way to know in advance what is going to be used until it’s actually needed. – Synetech Apr 17 '10 at 15:09
  • 1
    @Synetech What, at runtime? If you don't know you need the symbol, how can it possibly be removed? –  Apr 17 '10 at 15:21
  • No at compile-time. I can’t know in advance what constants I will use. And since there is more than just the two I gave in the example, it would be a huge pain to create a define for each and every one. – Synetech Apr 17 '10 at 15:39
0

On Windows, especially using MFC, the answer is to put the strings into resources, and load the string from there using LoadResource / LoadString (see http://msdn.microsoft.com/en-us/library/a44fb3wy(VS.80).aspx)

In your case, you have two constant CStrings, So the resource compier will generate something like

#define IDS_COMPANY 1
#define IDS_DIGITS  2

So in this case you would have a .dll with two strings. You app would load the .dll (AfxLoadLibrary). Then when you need the string your app loads it from the resource .dll.

This is what keeps Windows apps from bloating with global static strings, bitmaps, etc.

Lance Diduck
  • 1,513
  • 9
  • 11
  • Thanks for the quick tutorial on doing that, but it doesn’t really help here. (1) there are more than just the two in the example, (2) putting them in resources makes it a big pain to add, edit, and delete them, (3) it also makes it a big pain to include them in multiple projects (unlike adding `#include "common.h"` to the top of the file (I’ve run into trying to use common resources in multiple apps before—blech!), and (4) the DLL would need to be given its own project and recompiled separately unlike a simple header file. – Synetech Apr 17 '10 at 16:22