From: richard@iesd.auc.dk (Richard Flamsholt S0rensen)
Newsgroups: comp.os.msdos.programmer
Subject: >64K string literals under Windows (summary)
Date: 5 Oct 92 00:20:28 GMT
Distribution: comp
Organization: Mathematics and Computer Science, Aalborg University


  Hi all.

  Recently I asked what to do when compiling code with Borland C
under Window 3.1 and your data segment (static data) got larger than
64K. My problem was that it was a bunch of literal strings that
filled the data segment and neither could they be put into a segment
of their own nor could the offending module be compiled as huge
because Windows doesn't allow this.

  I got several replies (>10) listing these solutions:

	- use resources to hold the strings
	- compile the offending module as a DLL
	- place strings in a textfile and read it runtime, use
	  named char[]'s instead of literals, and the like.

  Most people voted for using resources.  DLL's came in as number two.
I decided to use resources, since  1) it seems the best solution and
2) I already have all my strings collected in a single file.

  This is, however, not a general solution, since it requires
modifying the source.  I forgot to mention in my original posting
that I only *linked* with a lot of the code -- I could not change
(well, practically not) the source and thus could not use resources
to remove *these* strings from the data segment.  Luckily, they don't
amount to that much, but if they did, I'd had to compile those
modules as DLLs.

  So, here's the summary of what to do when your literals causes the
data segment to exceed 64K and what you might as well forget to try:

	- No, you cannot compile modules huge under Windows.  You can,
	  however, typemodify your largest static data structures to
	  to be "huge" to give you some more room.
	- No, you cannot tell Borland C to store literals in another
	  segment.
	- Therefore, either move all your strings into a resource file
	  or compile the module as a DLL.  These seems the only good
	  solutions.  Sorry.

  This seems to be a general problem and one emailer reported that
they'd had to break their program into three pieces for this excact
reason.  My advice would therefore be to always place your literals
in a resource file from the very beginning of a project.


  The only (but a major) problem is then to give the constants some
information-filled names and to maintain the resource files.  You
will wind up with two files:  one for #defined constants to use in
your program, and one resource file.  Here's how I tackle the
problem, illustrated by strings.h and strings.rc:

strings.h:
~~~~~~~~~~~
#define TXTsd_CANNOT_OPEN_DATABASE	1
#define TXTs_REMOVING_USER_ENTRY	2
#define TXT_SOME_SILLY_TEXT		3

strings.rc:
~~~~~~~~~~~
begin stringtable
  TXTsd_CANNOT_OPEN_DATABASE	"Cannot open database \"%s\" (error %d)"
  TXTs_REMOVING_USER_ENTRY	"Removing user %s ..."
  TXT_SOME_SILLY_TEXT		"Some silly text  :-b"
end stringtable

  First, I adapt a common prefix for those constants, i.e. "TXT".
Second, included in the name of the constant is an indication of the
formatting parameters used by that string, e.g. "sd".  This makes it
easier to check whether a format string is given the correct parameters
when it is used without having to look it up.  Third, the name closely
resembles the contents -- don't use "STRING34" ...

  As the number of strings grows, it becomes cumbersome to maintain a
proper correspondance between constant and string if they are in two
different files.  I suggest you put them into one file and excercises
that file with some preprocessing to generate these two files.  You
could, for instance have a file like this and run it through the m4
macro preprocessor (Why not plain cpp?  Well, it has a problem outputting
#'s, like when you want to output "#define X 4").

strings.m4:
~~~~~~~~~~~
maketext(TXTsd_CANNOT_OPEN_DATABASE, "Cannot open database \"%s\" (error %d)")
maketext(TXTs_REMOVING_USER_ENTRY, "Removing user %s ...")
maketext(TXT_SOME_SILLY_TEXT, "Some silly text  :-b")

-where "maketext" is a macro producing different output depending on
what file to produce.  Here's an example:

To produce strings.h:
~~~~~~~~~~~~~~~~~~~~~
ifdef(H_FILE,
	`define(`maketext', `#define' $1 <count>)'
)
[...all the maketext()'s above]

[This is pseudo-m4, which outputs "#define <CONSTANT> n".  Problem is,
I haven't figured out how to place code to increment a variable in a
function in m4 yet without that either happening immediately when the
maketext is defined or not happening at all.]

To produce strings.rc:
~~~~~~~~~~~~~~~~~~~~~~
ifdef(`RC_FILE',
	`define(`maketext',STRING $1 $2)'
)
begin stringtable
  [...all the maketext()'s above]
end stringtable
[Again, this is m4 to output "STRING <CONSTANT> <STRING LITERAL>"]

  Oh well, you get the general idea.  Now, your constant name and string
literal are close together and it makes it soooo much easier to maintain.


  Thanks to all who replied -- let's roll the credits:

wilcox, swarm.wustl.edu (Don Wilcox)
Jeff Dean <jeff@igc.apc.org>
markg@county.lmt.mn.org (Mark Gilbert)
brown@ncoast.org (Stan Brown)
johnnyl@microsoft.com
John DeGood <degood@avo.hp.com>
Jim Thomas <jim@edscom.demon.co.uk>
sammy@sed.csc.com (Sam Blackburn)
raymondc@microsoft.com
jamshid@emx.cc.utexas.edu (Jamshid Afshar)
[and a few more I don't have the names for right here]


  Richard

  PS: no need to email me questions etc, since I'll loose this account
within 8 hours.  Goodbye, cruel, non-university world ... [you are,
however, most welcome to write a lot of letters to iesd.auc.dk
pleading for me to keep my username one more year :-)]
--
Richard Flamsholt
richard@iesd.auc.dk