Sun
12
Nov 2017
I recently updated my CommonLib library to support 64-bit code. This is a change that I planned for a very long time. I written this code so many years ago that 64-bit compilation on Windows was not popular back then. Today we have opposite situation - a modern Windows application or game could be 64-bit only. I decided to make the library working in both configurations. The way I did it was:
The most important and also most frequent change I needed to make in the code was to use size_t
type for every size (like the number of bytes), index (for indexing arrays), count (number of elements in some collection), length (e.g. number of characters in a string) etc. This is the "native" type intended for that purpose and as such it's 32-bit or 64-bit, depending on configuration. This is also the type returned by sizeof
operator and functions like strlen
. Before making this change, I carelessly mixed size_t
with uint32
.
I sometimes use maximum available value (all ones) as a special value, e.g. to communicate "not found". Before the change I used UINT_MAX
constant for that. Now I use SIZE_MAX
.
I know there is a group of developers who advocate for using signed integers for indices and counts, but I'm among the proponents of unsigned types, just like C++ standard recommends. In those very rare situations where I need a signed size, correct solution is to use ptrdiff_t
type. This is the case where I apply the concept of "stride", which I blogged about here (Polish) and here.
I have a hierarchy of "Stream" classes that represent a stream of bytes that can be read from or written to. Derived classes offer unified interface for a buffer in memory, a file and many other. Before the change, all the offsets and sizes in my streams were 32-bit. I needed to make a decision how to change them. My first idea was to use size_t
. It seems natural choice for MemoryStream
, but it doesn't make sense for FileStream
, as files can always exceed 4 GB size, regardless of 32/64-bit configuration of my code. Such files were not supported correctly by this class. I eventually decided to always use 64-bit types for stream size and cursor position.
There are cases where my usage of size_t
in the library interface can't work down to the implementation, because underlying API supports only 32-bit sizes. That's the case with zlib (a data compression library that I wrapped into stream classes in files ZlibUtils.*), as well as BSTR (string type used by Windows COM that I wrapped into a class in files BstrString.*). In those cases I just put asserts in the code checking whether actual size doesn't exceed 32-bit maximum before downcasting. I know it's not the safe solution - I should report error there (throw an exception), but well... That's only my personal code anyway :)
BstrString::BstrString(const wchar_t *wcs, size_t wcsLen) { (...) assert(wcsLen < (size_t)UINT_MAX); Bstr = SysAllocStringLen(wcs, (UINT)wcsLen);
From other issues with 64-bit compatibility, the only one was following macro:
/// Assert that ALWAYS breaks the program when false (in debugger - hits a breakpoint, without debugger - crashes the program). #define ASSERT_INT3(x) if ((x) == 0) { __asm { int 3 } }
The problem is that Visual Studio offers inline assembler in 32-bit code only. I needed to use __debugbreak
intrinsic function from intrin.h
header instead.
Overall porting of this library went quite fast and smoothly, without any major issues. Now I just need to port the rest of my code to 64 bits.
Comments | #visual studio #c++ #commonlib Share