Vault
4.1
|
The file vtypes.h and the platform-specific version of the file it includes, vtypes_platform.h, define a core set of basic data types and preprocessor macros that facilitate platform-neutral code. More...
Defines | |
#define | Vx32_IS_xINT |
#define | NULL 0 |
Definition of NULL in compiler environments that don't already define it. | |
#define | CONST_S64(s) /*lint -save -e961*/ s##LL |
Macro to declare a Vs64 constant in a way that works even in VC++ 6./*lint -restore*/. | |
#define | CONST_U64(s) s##ULL |
Macro to declare a Vu64 constant in a way that works even in VC++ 6. | |
#define | V_BYTESWAP_HTON_S16_GET(x) (x) |
#define | V_BYTESWAP_NTOH_S16_GET(x) (x) |
#define | V_BYTESWAP_HTON_S16_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_S16_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_U16_GET(x) (x) |
#define | V_BYTESWAP_NTOH_U16_GET(x) (x) |
#define | V_BYTESWAP_HTON_U16_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_U16_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_S32_GET(x) (x) |
#define | V_BYTESWAP_NTOH_S32_GET(x) (x) |
#define | V_BYTESWAP_HTON_S32_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_S32_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_U32_GET(x) (x) |
#define | V_BYTESWAP_NTOH_U32_GET(x) (x) |
#define | V_BYTESWAP_HTON_U32_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_U32_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_S64_GET(x) (x) |
#define | V_BYTESWAP_NTOH_S64_GET(x) (x) |
#define | V_BYTESWAP_HTON_S64_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_S64_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_U64_GET(x) (x) |
#define | V_BYTESWAP_NTOH_U64_GET(x) (x) |
#define | V_BYTESWAP_HTON_U64_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_U64_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_F_GET(x) (x) |
#define | V_BYTESWAP_NTOH_F_GET(x) (x) |
#define | V_BYTESWAP_HTON_F_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_F_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_HTON_D_GET(x) (x) |
#define | V_BYTESWAP_NTOH_D_GET(x) (x) |
#define | V_BYTESWAP_HTON_D_IN_PLACE(x) ((void)0) |
#define | V_BYTESWAP_NTOH_D_IN_PLACE(x) ((void)0) |
#define | CLASS_CONST(type, name, init) static const type name = (init) |
Macro to declare a class static constant in a way that works even in VC++ 6. | |
Typedefs | |
typedef int8_t | Vs8 |
Signed 8-bit integer. | |
typedef uint8_t | Vu8 |
Unsigned 8-bit integer. | |
typedef int16_t | Vs16 |
Signed 16-bit integer. | |
typedef uint16_t | Vu16 |
Unsigned 16-bit integer. | |
typedef int32_t | Vs32 |
Signed 32-bit integer. | |
typedef uint32_t | Vu32 |
Unsigned 32-bit integer. | |
typedef int64_t | Vs64 |
Signed 64-bit integer. | |
typedef uint64_t | Vu64 |
Unsigned 64-bit integer. | |
typedef float | VFloat |
Single-precision floating-point number. | |
typedef double | VDouble |
Double-precision floating-point number. | |
typedef Vs64 | VFSize |
Container for file or stream sizes. The purpose is to prevent 32-bit limits from creeping into APIs and source code. | |
typedef size_t | VSizeType |
loop index variable of correct type for STL iteration |
The file vtypes.h and the platform-specific version of the file it includes, vtypes_platform.h, define a core set of basic data types and preprocessor macros that facilitate platform-neutral code.
Without these definitions, you would have to cope with the various quirks and behaviors of different compilers, libraries, and processors.
Whenever possible, you should just use int. If you are willing to assume that the world is now 32-bit or better, then you can treat int as "a signed integer that holds about +/- 2 billion", and if your data fits into that description, int is good because it is signed and general. However, there are cases where you need to use specific sized integer, perhaps unsigned, and the types Vs8, Vu8, Vs16, Vu16, Vs32, Vu32, Vs64, and Vu64 provide signed and unsigned definitions for 8, 16, 32, and 64 bit integers. For example, if you are doing binary stream i/o, you'd better be certain of the exact number of bytes used in the stream to represent each particular integer value; thus, VBinaryIOStream speaks in these types and does not let you read and write the more ambiguous "int". The definition of a 64-bit integer is different across compilers, hence it is unsafe for you to use "long long" and expect it to work; thus the existence of Vs64 and Vu64, which work correctly even with MSVC++ 6.
Because the single-precision "float" type in the language is ambigous and essentially dangerous, vtypes defines VDouble, which uses double precision. There is also a VFloat type, but in general you should avoid it because of loss of precision and the aforementioned dangers.
If you want to avoid writing code that breaks when encountering large files, it is best to use the type VFSize to represent file and stream sizes. Even in today's systems, some users work with files whose size exceeds what can be represented in 32 bits. A video file of 3GB is not that uncommon. You need 64 bits, and that's how VFSize is defined. Alternatively, you could just use Vs64 (or even Vu64), and that would work just as well, although it reads as less obvious what its purpose is. The somewhat standard type size_t is useful, but it will not be 64 bits on most systems today, so the Vault does not use it to define stream-related sizes, in favor of a uniform use of VFSize.
The type VSizeType is defined as a uniform way of defining a type for the size of an STL collection. This is often a good clean way of defining the loop control variable for an STL iteration for-loop. You cannot just use "int" for those situations because it's the wrong type and strong compiler type-checking will complain; and it seems awkward to use a specific STL template-defined size_type because if you decide to use a different STL collection type, your control variable type should change, too. I'm not totally satisfied with this, but so far this seems like the best option.
See VString for the string class used everywhere in the Vault.
See VInstant and its relatives VDate and VTimeOfDay for the date and time representations used everywhere in the Vault.
Some compilers' libraries provide macros for invoking host-to-network order and network-to-host order byte swapping, such that they do nothing on "big endian" machines ("network byte order" means big endian in this terminology). However, not all such libraries provide these macros and functions, or don't provide them for all data types; so here we define a complete set of macros for all fundamental data types.
In general, you should use VBinaryIOStream to read and write data in a byte-order-neutral fashion. In rare cases you may want to make sure your data is in host or network byte order, in which case you would use one of the V_BYTESWAP_* macros, all of which do the right thing -- which is "nothing" if the host order is network order. Finally, if you really need to do byte swapping explicitly, you can call one of the byte-swapping functions VbyteSwap16(), VbyteSwap32(), VbyteSwap64(), VbyteSwapFloat(), or VbyteSwapDouble() which swap bytes between network and host order (the two directions happen to be symmetric for big- and little-endian). These functions always swap the bytes, in contrast to the macros which are no-ops when running on a big-endian system.
There are a couple of things to note about defining and using constants.
Because of certain compilers' limitations (namely MSVC++ 6), you cannot declare a 64-bit constant in the normal way if you want it to compile cross-platform. That is, "1LL" is unfortunately not going to work. Provided here is a macro CONST_S64 that will work with all compilers. So use "CONST_S64(1)" instead, to declare a 64-bit constant whose value is 1. Use CONST_U64 for unsigned values.
MSVC++ 6 also is incompatible with the normal way of declaring class static constants (e.g., "const int kMyConstant = 42;"). The Vault provides a macro called "CLASS_CONST" that allows you to declare class static int constants in a way that will compile with MSVC++ 6: "CLASS_CONST(int, kMyConstant, 42);". As of Code Vault 2.5, I am no longer using this macro, but it's still defined in case you need it (please just dump the piece of junk MSVC++ 6 compiler and get the freely downloadable and reasonably decent VC++ 8 instead).
There are constants defined for the miniumum and maximum possible values of each integer data type: V_MIN_S8, V_MAX_S8, V_MAX_U8, V_MIN_S16, V_MAX_S16, V_MAX_U16, V_MIN_32, V_MAX_S32, V_MAX_U32, V_MIN_64, V_MAX_S64, V_MAX_U64. These are defined as Vs64 values so that they are always valid and interchangeable. You should use these in place of the typical POSIX macros because not only are the POSIX macros not available on all platforms, but they aren't necessarily provided for 64-bit values; and you should use them instead of the standard C++ function templates may not be available at all.
There are macros defined to wrap min(), max(), abs(), and fabs(), such that they will still work if the standard C++ function templates are not available: V_MIN, V_MAX, V_ABS, V_FABS. You should use these if you want to be sure your code compiles cross-platform.
A macro ASSERT_INVARIANT is defined that calls "this->assertInvariant()" if you are building in debug mode. This allows you to define a method assertInvariant() in any class, and call this macro to test your invariants. The rule of thumb is that you should test the invariants at the end of every (public) constructor, test the invariants on entry to each method, test the invariants on exit of any non-const method. See "C++ FAQs" item 224. The Vault uses this mechanism itself in several of its classes that are suited to invariant testing.