Add workaround for Win32 wrong type for size_t

GCC std::array uses std::size_t for its size_type.  On Linux/amd64,
`std::size_t` is `unsigned long`.  On Win32, `std::size_t` is
`unsigned int`.  This provokes format string warnings because an
unsigned int is passed where the format string declares an unsigned
long.  On Win32, `unsigned int` and `unsigned long` are the same size,
so `unsigned long` could have been used to avoid this problem by
maintaining consistency with Linux, but it was not.  This may have been
an attempt to achieve bug compatibility with Microsoft's types.

Add a workaround by defining a macro DXX_PRI_size_type in the style of
inttypes.h PRI* macros.  Use an SConf test to determine the correct
value by inspecting which compilation runs succeed.  Currently, Linux
needs "l", Win32 needs "", and Win64 needs "I64".
This commit is contained in:
Kp 2016-07-15 03:43:03 +00:00
parent a9aa5552b7
commit 674a9213f8
2 changed files with 98 additions and 4 deletions

View file

@ -1744,6 +1744,100 @@ help:always wipe certain freed memory
poison = True
if poison:
context.sconf.Define('DXX_HAVE_POISON')
implicit_tests.append(_implicit_test.RecordedTest('check_size_type_long', "assume size_t is formatted as `unsigned long`"))
implicit_tests.append(_implicit_test.RecordedTest('check_size_type_int', "assume size_t is formatted as `unsigned int`"))
implicit_tests.append(_implicit_test.RecordedTest('check_size_type_I64', "assume size_t is formatted as `unsigned I64`"))
@_custom_test
def _check_size_type_format_modifier(self,context,_text='''
#include <cstddef>
#define DXX_PRI_size_type %s
__attribute_format_printf(1, 2)
void f(const char *, ...);
''',_main='''
std::size_t s = 0;
f("%" DXX_PRI_size_type, s);
'''):
'''
The test must declare a custom function to call with this format string.
gcc has hardcoded knowledge about how printf works, but that knowledge
on Mingw64 differs from the processing of
__attribute__((format(printf,...))). Mingw64 requires I64u for
__attribute__((format)) functions, but llu for printf. Mingw32 is
consistent in its use of u. Linux gcc is consistent in its use of
lu.
-- cut --
#include <cstdio>
#include <cstddef>
__attribute__((format(printf,1,2))) void f(const char *,...);
void a() {
std::size_t b = 0;
printf("%I64u", b);
f("%I64u", b);
printf("%llu", b);
f("%llu", b);
printf("%lu", b);
f("%lu", b);
printf("%u", b);
f("%u", b);
}
-- cut --
$ x86_64-w64-mingw32-g++-5.4.0 -x c++ -S -Wformat -o /dev/null -
<stdin>: In function 'void a()':
<stdin>:6:19: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long long unsigned int}' [-Wformat=]
<stdin>:9:13: warning: unknown conversion type character 'l' in format [-Wformat=]
<stdin>:9:13: warning: too many arguments for format [-Wformat-extra-args]
<stdin>:10:17: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka long long unsigned int}' [-Wformat=]
<stdin>:11:12: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka long long unsigned int}' [-Wformat=]
<stdin>:12:16: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long long unsigned int}' [-Wformat=]
<stdin>:13:11: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long long unsigned int}' [-Wformat=]
$ i686-w64-mingw32-g++-5.4.0 -x c++ -S -Wformat -o /dev/null -
<stdin>: In function 'void a()':
<stdin>:7:14: warning: format '%I64u' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:8:18: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:9:13: warning: unknown conversion type character 'l' in format [-Wformat=]
<stdin>:9:13: warning: too many arguments for format [-Wformat-extra-args]
<stdin>:10:17: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:11:12: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
$ mingw32-g++-5.4.0 -x c++ -S -Wformat -o /dev/null -
<stdin>: In function 'void a()':
<stdin>:6:19: warning: format '%I64u' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:7:14: warning: format '%I64u' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:8:18: warning: unknown conversion type character 'l' in format [-Wformat=]
<stdin>:8:18: warning: too many arguments for format [-Wformat-extra-args]
<stdin>:9:13: warning: unknown conversion type character 'l' in format [-Wformat=]
<stdin>:9:13: warning: too many arguments for format [-Wformat-extra-args]
<stdin>:10:17: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
<stdin>:11:12: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'std::size_t {aka unsigned int}' [-Wformat=]
$ x86_64-pc-linux-gnu-g++-5.4.0 -x c++ -S -Wformat -o /dev/null -
<stdin>: In function 'void a()':
<stdin>:6:19: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
<stdin>:7:14: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
<stdin>:8:18: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
<stdin>:9:13: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
<stdin>:12:16: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
<stdin>:13:11: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'std::size_t {aka long unsigned int}' [-Wformat=]
'''
# Test types in order of decreasing probability.
for how in (
# Linux
('l', 'size_type_long'),
# Win64
('I64', 'size_type_I64'),
# Win32
('', 'size_type_int'),
):
DXX_PRI_size_type = how[0]
f = '"%su"' % DXX_PRI_size_type
if self.Compile(context, text=_text % f, main=_main, msg='whether to format std::size_t with "%%%su"' % DXX_PRI_size_type, calling_function=how[1]):
context.sconf.Define('DXX_PRI_size_type', f)
return
raise SCons.Errors.StopError("C++ compiler rejects all candidate format strings for std::size_t.")
@_custom_test
def check_strcasecmp_present(self,context,_successflags={'CPPDEFINES' : ['DXX_HAVE_STRCASECMP']}):
main = '''

View file

@ -471,12 +471,12 @@ void bm_read_extra_robots(const char *fname, Mission::descent_version_type type)
t = PHYSFSX_readInt(fp);
if (N_D2_OBJBITMAPS+t >= ObjBitmaps.size())
Error("Too many object bitmaps (%d) in <%s>. Max is %lu.", t, fname, ObjBitmaps.size() - N_D2_OBJBITMAPS);
Error("Too many object bitmaps (%d) in <%s>. Max is %" DXX_PRI_size_type "u.", t, fname, ObjBitmaps.size() - N_D2_OBJBITMAPS);
bitmap_index_read_n(fp, partial_range(ObjBitmaps, N_D2_OBJBITMAPS, N_D2_OBJBITMAPS + t));
t = PHYSFSX_readInt(fp);
if (N_D2_OBJBITMAPPTRS+t >= ObjBitmapPtrs.size())
Error("Too many object bitmap pointers (%d) in <%s>. Max is %lu.", t, fname, ObjBitmapPtrs.size() - N_D2_OBJBITMAPPTRS);
Error("Too many object bitmap pointers (%d) in <%s>. Max is %" DXX_PRI_size_type "u.", t, fname, ObjBitmapPtrs.size() - N_D2_OBJBITMAPPTRS);
range_for (auto &i, partial_range(ObjBitmapPtrs, N_D2_OBJBITMAPPTRS, N_D2_OBJBITMAPPTRS + t))
i = PHYSFSX_readShort(fp);
}
@ -537,7 +537,7 @@ void load_robot_replacements(const d_fname &level_name)
for (j=0;j<t;j++) {
i = PHYSFSX_readInt(fp); //read objbitmap number
if (i < 0 || i >= ObjBitmaps.size())
Error("Object bitmap number (%d) out of range in (%s). Range = [0..%lu].", i, static_cast<const char *>(level_name), ObjBitmaps.size() - 1);
Error("Object bitmap number (%d) out of range in (%s). Range = [0..%" DXX_PRI_size_type "].", i, static_cast<const char *>(level_name), ObjBitmaps.size() - 1);
bitmap_index_read(fp, ObjBitmaps[i]);
}
@ -545,7 +545,7 @@ void load_robot_replacements(const d_fname &level_name)
for (j=0;j<t;j++) {
i = PHYSFSX_readInt(fp); //read objbitmapptr number
if (i < 0 || i >= ObjBitmapPtrs.size())
Error("Object bitmap pointer (%d) out of range in (%s). Range = [0..%lu].", i, static_cast<const char *>(level_name), ObjBitmapPtrs.size() - 1);
Error("Object bitmap pointer (%d) out of range in (%s). Range = [0..%" DXX_PRI_size_type "].", i, static_cast<const char *>(level_name), ObjBitmapPtrs.size() - 1);
ObjBitmapPtrs[i] = PHYSFSX_readShort(fp);
}
Robot_replacements_loaded = 1;