BCB2006: memset, std namespace, std header idempotence, bug?

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Wed, 22 Feb 2006 20:37:50



Dear All

The following code should compile, but does not:

//---------------------------------------------------------------------------
#include <cstring>
#include <string.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
memset(0, 0, 1);// E2268 Call to undefined function 'memset'
return 0;
}

Remove the #include <cstring> line and it works. OK, the example is contrived, but it's the simplest distillation of the problem I could come up with.

This seems to be happening because both cstring and string.h eventually include the same file, mem.h. Like many of the actual standard library implementation files in the BCB2006 this file is *not* idempotent: right at the end of the file, after the header guards, it contains the following code:

#if defined(__cplusplus) && !defined(__USING_CNAME__) && !defined(__MEM_H_USING_LIST)
#define __MEM_H_USING_LIST
using std::_wmemchr;
using std::_wmemcpy;
using std::_wmemset;
using std::memccpy;
using std::memicmp;
using std::memmove;
using std::memchr;
using std::memcmp;
using std::memcpy;
using std::memset;
#endif /* __USING_CNAME__ */

This code is clearly designed to bring the names declared in mem.h into the global namespace when required. When cstring includes mem.h it does not define __USING_CNAME__. When string.h includes mem.h it does define __USING_CNAME__.

The problem appears to be that at some level the compiler is assuming that mem.h is actually idempotent and is not bothering to process it a second time. So memset and the other names never get pushed into the global namespace even though they should.

I believe this is a bug, and one that affects large numbers of standard header files. What do you think?

Kind regards

Garry Lancaster
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Wed, 22 Feb 2006 21:06:14


It's actually the other way around: including cstring does
define __USING_CNAME__, including string.h does not. (And by
the way, after the first inclusion of mem.h, __USING_CNAME__
is undefined, so that's not the cause of the problem.)

Sorry for any confusion. The rest of my original post stands
and I still think it's a compiler bug.

Kind regards

Garry Lancaster

 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Ed Mulro » Wed, 22 Feb 2006 22:16:37

memset is part of the namespace "std"
Use
std::memset
or put in this statement
using std::memset;
or this one
using namespace std;


Header files are configured with header guards, configured in a manner like
this:

----beginning of file-----
#ifndef ANAME
#define ANAME
---contents of file---
#endif
---end of file----

The second time the file is included, be it under the proper name cstring or
with the deprecated name string.h, the header guards suppress expansion of
the file contents into the preprocessed information given to the compiler
phase.

. Ed
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Wed, 22 Feb 2006 23:14:10


Right as far as it goes, but, crucially, the string.h header should place memset into the global namespace too. This is a
C compatibility feature of C++ (C did not have namespaces).


In other words, you think the mem.h file is idempotent
because of header guards. That's not entirely correct,
as I explained in the OP:

^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^

[non header-guard controlled code snipped]

If you look at the bottom of your own copy of BCB2006's
mem.h, if you have one, you will see what I'm getting at:
the #endif of the header guard is not at the end of the
file.

The using declarations at the bottom of mem.h are not
controlled by header guards. The compiler acts like they
are though, which is the problem.

Kind regards

- Garry Lancaster
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by David Dea » Thu, 23 Feb 2006 00:28:05

In article <43fb1046$ XXXX@XXXXX.COM >,



It isn't a compiler bug. You are making a mistake thinking that
memset should be in the global namespace in this context.

--
-David

Nihil curo de ista tua stulta superstitione.
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by David Dea » Thu, 23 Feb 2006 00:41:23

In article <43fb2e42$ XXXX@XXXXX.COM >,



He wasn't talking about mem.h, but about string.h. string.h is
included in cstring, so when you include string.h after cstring,
string.h is not loaded again because of the header guards in string.h

--
-David

Nihil curo de ista tua stulta superstitione.
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Thu, 23 Feb 2006 01:21:29


The relevant part of the C++ Standard is D.5 para 2:

"Each C header, whose name has the form name.h, behaves
as if each name placed in the Standard library namespace
by the corresponding cname header is also placed within
the namespace scope of the namespace std and is followed
by an explicit using-declaration (7.3.3)"

This means that, for example, including string.h should
place memset inside std and then add a using-declaration

using std::memset;

which would add memset to the global namespace (or it
should behave as if this were done).

Actually, looking in more detail at the header files
I think I've found the cause of the strange behaviour
BCB2006 is exhibiting. It's not that mem.h has header
guards (it does, but not around the using declarations)
or that string.h has header guards (it doesn't), it's
that _str.h does.

The inclusion chain is:

string.h includes _str.h includes mem.h

In fact _str.h has both internal guards and external
guards around mem.h. Because of these, mem.h is
effectively being made idempotent.

Normally, we want header files to be idempotent, but
in this case it's causing a deviation from
the correct behaviour.

Kind regards

Garry Lancaster
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by maede » Thu, 23 Feb 2006 02:26:01

"Garry Lancaster" < XXXX@XXXXX.COM > writes:


[snip]



I agree, this is an error.

#include <string.h> is supposed to do everything <cstring> does, and
to import the names declared in <cstring> into the global
namespace. Which is not what's going on here.

I am not convinced that the compiler assumes anything it
shouldn't. It's rather the conditional compilation scaffolding that I
don't trust; it is too complex to my taste.
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Ed Mulro » Thu, 23 Feb 2006 04:54:00

I do not think anything is "idempotent" and doubly so when the word is used
to apply to a header file.

relating to or being a mathematical quantity which when
applied to itself under a given binary operation (as
multiplication) equals itself; also : relating to or being an
operation under which a mathematical quantity is
idempotent

I said that the header file when first included was done so as cstring in
the manner used by C++ so memset was declared in the std namespace. The
second inclusion as string.h does not expand because the header guards block
a second expansion of a file which has already been included.

String.h is not a standard C++ header but rather a C header that is allowed
but depricated. There is nothing in the C++ standard which suggests let
alone requires that a standard C header file which has already been included
in the C++ fashion and is now included with the depricated C fashion be
handled in the manner you describe.

. Ed
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by David Dea » Thu, 23 Feb 2006 05:13:19

In article <43fb2e42$ XXXX@XXXXX.COM >,



Can you reduce this down to an example that isn't dependent on the
standard library header files. This will make it easier to follow your
logic.

--
-David

Nihil curo de ista tua stulta superstitione.
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Thu, 23 Feb 2006 18:16:14


Yes, that's the conclusion I've come to now. Thanks
for an excellent and concise summary.

Kind regards

Garry Lancaster
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Garry Lanc » Thu, 23 Feb 2006 18:54:08


When applied to header files, idempotence means that
multiple inclusions of the header file in a single
translation unit have the same effect as a single
inclusion.

Most header files are made idempotent using header
guards.

A supporting link:

http://www.yqcomputer.com/ (software)

The problem here, is that there is a header file, mem.h,
which is not designed to be idempotent, but which is
being externally wrapped in header guards.


I agree with you that the behaviour of header guards
is important here. However, string.h is only included once,
as is cstring. The relevant file that is included twice
is _str.h.


string.h is a standard header in both the C and C++
languages. It is indeed deprecated in the C++ Standard.
However, when the C++ Standard says something is
"deprecated", it is still a requirement for the current
version of the Standard, but may not be included in a
future revision (this is stated in Appendix D, para 2
of the C++ Standard).

I previously posted the part of the C++ Standard that states
the required behaviour of string.h (D.5 para 1).

Kind regards

Garry Lancaster
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Tom Widme » Thu, 23 Feb 2006 19:26:44


<string.h> is a standard C++ header that is *required* (but deprecated)
for a hosted implementation.

There is nothing in the C++ standard which suggests let

"Every C header, each of which has a name of the form name.h, behaves as
if each name placed in the Standard library namespace by the
corresponding cname header is also placed within the namespace scope of
the namespace std and is followed by an explicit using-declaration (7.3.3)."

It doesn't say that it won't do this if the <cheader> version is already
included, which would of course introduce a crazy dependency on order of
inclusion.

Tom
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Alan Belli » Thu, 23 Feb 2006 20:11:35


If that's a dictionary definition, then it's missing the C++ header file
sense. But then, dictionaries do miss things, sometimes for decades and
especially in specialised fields. (There's a fun series on BBC TV at the
moment, wherein a presenter tries to get entries in the current Oxford
English Dictionary updated. It's interesting to see that the OED itself
thinks that many words and phrases came into existence years after I,
for one, first encountered them. Its history of the phrase 'The Full
Monty' pretty well starts with the film of the same name, though there
was at least a cafe with the name decades earlier.)

In the sense used here, the term 'idempotent' means that the effect of
the inclusion of a header file does not depend on the pre-inclusion of
any other header file, including itself. This is an important concept,
and one that all the standard header files aspire to (aspire, because
there's not always a guarantee. Preincluding a file that does a "#define
void int" is too much to attempt to defend against.)


[depr.c.headers] D.5/2: "Each C header, whose name has the form name.h,
behaves as if each name placed in the Standard library namespace by the
corresponding cname header is also placed within the namespace scope of
the namespace std and is followed by an explicit using-declaration
(7.3.3)"

So, your description of the mechanics may well be correct, but the
wording of the standard requires the using directive to occur (or an
action that is as-if). Yes, that's in the deprecated section, but until
the standard actively says otherwise, it still applies.

In this case, it definitely does sound like a bug. Yes, including both
<string.h> and <cstring> does sound rather daft, but it's quite possible
for this inclusion to be masked by actually being done by other header
files. For instance:

// my_header.h
#include <string.h> // for size_t
size_t first_offset(char const* src, char const* tgt);

// her_header.h
#include <cstring> // for std::string
std::string module_name();

// code.cpp
#include "her_header.h"
#include "my_header.h"
... ooops

There are no problems in including either header on its own. There
should be no problem about including both.

(BTW - Garry is, or at least has been, an active member of the UK C++
panel, so he should be somewhat in the same category as Alisdair -
perfectly capable of making mistakes, of course, but not a newbie.)

Alan Bellingham
--
ACCU Conference 2006 - 19-22 April, Randolph Hotel, Oxford, UK
 
 
 

BCB2006: memset, std namespace, std header idempotence, bug?

Post by Ed Mulro » Thu, 23 Feb 2006 22:33:01

> If that's a dictionary definition ...

http://www.yqcomputer.com/

. Ed