How to write generic compiler-independent code? - c++

In my work I work with many different compilers for many platforms (Windows, embedded microcontrollers, Arduino, etc).
Now I want to write a generic routine that should work with all of them, but I'm getting conflicts with data types.
It's mostly low-level stuff, so I would like to work with types like 'byte', 'word', 'bool' etc.
For some compilers these types are not yet defined, but for some they are and in these cases that will result in errors of conflicting types.
I have learned that typedef are prefered above #define.
And in this question it is made clear that there is no way to make a conditional typedef.
I already thought of using unique types like for example:
typedef unsigned char mybyte
typedef unsigned short int myword
etc...
But that would make my sourcecode look very ugly IMHO.

All platforms should support bool as it is a reserved keyword for a built-in type in C++.
The only platform I know of that has byte and word is Arduino. They are just typedef'ed aliases to uint8_t and unsigned int respectively. (Source)
If you have existing Arduino code that uses byte and word, the easiest solution would be to check if your code runs in the Arduino environment, and define the Arduino types yourself if that's not the case:
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <cstdint>
typedef uint16_t word; // or unsigned int, depending on your needs
typedef uint8_t byte;
#endif
However, my preferred solution is to just use the standard integers of stdint.h directly when I need a specific number of bits. Using byte and word just adds to the confusion, because it is non-standard. uint16_t tells you exactly what it is, you know exactly what the largest possible value is, and whether it's signed or not.

Related

C++ how to define custom atomic data type

I am reading Game Engine Architecture by Jason Gregory, and I am confused by a sentence in the book:
"...most game egines achieve source code portability by defining their own custom atomic data types. For example, at Naughty Dog we use the follow atomic data types:
*F32 is a 32-bit IEEE-754 floating-point value
*U8, I8, U16, I16, U32, I32, U64 and I64 are unsigned and signed 8-, 16, 32, and 64-bit integers, respectively..."
I have looked all over google and the web trying to find a way to define these kinda of data types. Is this usually done by just using #define directives to assign these values to whatever the value is, like this:
#define U8 __int8
ect..
If there is any link, book or advice anyone can offer to understand what he means by this, or how to set it up, I would appreciate it.
Using #define is definitively not a good idea in C++. Even in C, you can use typedef for types.
typedef unsigned __int8 U8;
However, as mentioned by Dave (see his link for complete list), you have atomic definitions in C++ such as:
std::atomic_uint_fast8_t
// or
std::atomic<std::uint_fast8_t>
// with the typedef:
typedef std::atomic_uint_fast8_t U8;
Yet, if you want to be a little less advanced, you can include the cstdint include, which is in most cases what will work on most computers:
#include <cstdint>
That gives you the standard [u]int[8,16,32,64]_t types. So the same type as above would be:
uint8_t my_var;
// if you really want to use a typedef:
typedef uint8_t U8;
U8 my_var;
These types are portable, without the need for an extra typedef.
For float and double, these are generally portable. What is not is the long double which is rarely used anyway. You could still have a typedef, just in case:
typedef float F32;
Then anywhere in your code, you MUST use those definitions and not the default C/C++ types (i.e.char, short, int, long are forbidden.)
Put all of those in a header that all the other C++ files include.
Update:
enough memory in each type
Obviously, if you use uint8_t, then you can be sure that you at least have an 8 bit number. It could be 16 bits too... (some processors are limited that way) Similarly, a uin32_t will have at least 32 bits.
It is possible to have a compile time check if you really want to make sure. That makes use of the sizeof() with a template. See here:
Compile-time sizeof conditional
Note that this is not specific to games. Any programming should careful choose their variable types. More and more people are making use of 64 bit integers to make sure they can support sizes over 2Gb (4Gb if you though of using an unsigned...)
FYI -- one of the European Ariane rockets (it was French at the time of the accident) was blown up because a variable was 8 bits when it should have been 16 bits. That gives you an idea why it's important...

How to check if uint8_t exists as a type, instead of unsigned char?

I have two compilers, one that recognizes uint8_t(GCC ARM-EABI), and one that doesn't(Renesas M16 Standard Toolchain).
The Renesas Toolchain is NOT ANSI C compliant, so you can throw out . So uint8_t, uint16_t,... aren't defined as existing types.
In order to maintain portability, I would like to have the same types(preferably uint8_t, due to the ambiguity of int).
Also my platforms are different size processors(ARM is 32 bit, and Renesas is 16 bit). Causing int to be different values.
Is there a way to check if uint8_t exists as a type?
And if not, declare it(and others uint16_t, uint32_t,...) as a type?
Is there a way to check if uint8_t exists as a type?
Use:
#include <stdint.h>
#ifdef UINT8_MAX
...
#endif
uint8_t is not a built-in type, it is defined in stdint.h. So it is not a matter of the compiler "recognising" uint8_t, but rather simply a case of making stdint.h available.
If your toolchain does not provide stdint.h, you can easily provide your own implementation, using the compiler documentation to determine the built-in types that correspond to the specific sizes. On the toolchain without stdint.h you simply provide your own to the project, on the toolchain with stdint.h you don't. That way the code (other than stdint.h itself) will be identical across platforms - you don't need to conditionally define uint8_t.
One problem you may come across (on some TI DSP's for example) is that memory may not be 8 bit addressable and a char will be 16 bit (or larger). In that case uint8_t or indeed any 8 bit integer type will not be supported at all. A char is always the smallest data type for the specific platform, but may be larger than 8 bit.
There are a few different ways to deal with this. In an open source project that needs to be portable, the common solution is to have a "configure script", which is run to setup the build system. It would then have something like HAVE_UINTX_TYPES that is set or not set in some config.h or similar [which was one of the results of the "configure script", and do something like this:
#include "config.h"
...
#ifndef HAVE_UINTX_TYPES
#include "uintx_types.h"
#endif
In a less "needs to run on almost anything" system, you could solve the same problem by simply have a -DHAVE_UINTX_TYPES as part of the flags to the compiler. And since you (presumably) have some part of the build system that sets different compile options, picks a different compiler, etc, for the two different builds, this shouldn't be a big issue to add.
And assuming that you are happy that your unsigned char is indeed 8 bits, you could also do have a uintx_types.h that contains something like this:
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
Another option is to not use uint8_t and uint16_t etc directly, but have your own definitions [and have these depend on the appropriate build setting for "is it ARM or Renesas", e.g. by using different include options]:
ARM/types.h:
typedef unsigned char u_int8;
typedef unsigned short u_int16;
typedef unsigned int u_int32;
Renesas/types.h:
typedef unsigned char u_int8;
typedef unsigned int u_int16;
typedef unsigned long u_int32;
If uint8_t doesn't exist, it's either because the implementation does not conform to C99, or because it has no type that meets the requirements. The latter probably means CHAR_BIT > 8 (which is vanishingly rare outside embedded systems).
#if __STDC_VERSION__ >= 199901L
#include <stdint.h>
#ifdef UINT8_MAX
// uint8_t exists
#else
// uint8_t doesn't exist in <stdint.h>
#else
// uint8_t probably doesn't exist because this isn't a C99 or better compiler
#endif
It's possible for an implementation that doesn't fully conform to C99 to provide <stdint.h> and uint8_t as an extension. That's difficult to detect, because there's no conditional #include directive; a #include either includes the requested header or fails.
But you can detect it by going outside the C language, using some kind of configuration script. If this compiles:
#include <stdint.h>
uint8_t dummy;
then uint8_t exists; otherwise it doesn't.
The main point of uint8_t is that it won't exist on platforms that don't support an unsigned integral type with exactly 8 bits. If unsigned char is acceptable, even though it might be larger than 8 bits, then don't use uint8_t. There's nothing to be gained by using unsigned char on one platform and uint8_t on another.

crossplatform 64 bit type

Is there a 64 bit type that in every OS(32/64 bit) and for every compiler has a size of 64?
The same question is also for 32 bit type. (It should be int?)
The origin of the question is : I am implementing the system which has 2 kinds of instructions :
32 bit
64 bit
I want to write something like:
typedef int instruction32bit;
typedef long long instruction64bit //it is not correct some system have sizeof(long long) = 128
You are looking for int64_t and int32_t, or their unsigned friends uint64_t and uint32_t. Include either cinttypes or cstdint.
If you want your code to be truly portable, then you probably want to typedef your own type, and use for example
typedef int32_t instruction32bit;
typedef int64_t instruction64bit;
This will work MOST of the time, but if it doesn't for a particular system/compiler/whatever, you can add do something like this:
#ifdef SOMEDEFINE
typedef long long int instruction64bit;
typedef int instruction32bit;
#else
typedef int32_t instruction32bit;
typedef int64_t instruction64bit;
#endif
Of course, for each model of compiler/OS (or group thereof) that doesn't support int32_t and int64_t, you probably will need a special #ifdef.
This is exactly what all truly portable code does, because no matter how much you find that "nearly all compilers do X", if you get your code popular enough, there's always someone who wants to compile the code with "Bob's Compiler Project" which doesn't have this feature. Of course, the other thing is to just leat those who use "Bob's compiler" edit the typedef itself, and not accept the "For Bob's compiler, you need this ..." patch that inevitably gets sent your way.
As Carl Norum points out in a comment, the #ifdef may be possible to convert to a #if in many cases, and then use generic types such as int and long.
Use uint_least32_t and uint_least64_t. The fixed-size types uint32_t and uint64_t will not exist on systems that don't have the exact sizes they describe.

u_int32_t vs bpf_u_int32

I've been busy doing some network programming over the past couple of days and I cant seem to figure out a difference between the data types u_int32_t abd bpf_u_int32.
u_int32_t means 32 unsigned bits. Doesnt bpf_u_int32 mean the same?
Because some functions read the IP address in one form or the other.
Some functions in the pcap library like pcap_lookupnet require the net address to be of the form bpf_u_int32.
I am curious to know the difference
Programmers add layers of indirection for a living. They're almost certainly the same type, you can check that in C++ with #include <typeinfo> followed by typeid(u_int32_t) == typeid(bpf_u_int32).
On some implementations there's at least the possibility that one is unsigned int and the other is unsigned long.
What's happened is that two different people have independently chosen a name for a 32 bit unsigned type (or maybe the same person for two slightly different purposes). One of them has used a "bpf" prefix, which in this context stands for Berkeley Packet Filter since that's relevant to packet capture. The other one hasn't. One has used the _t suffix that indicates a type name, the other hasn't. Aside from that, they picked similar names.
C99 and C++11 both introduce a standard name for a 32 bit unsigned type: uint32_t. That won't stop people creating their own aliases for it, though.
Both types are most likely typedefs to a 32-bit unsigned type. As such, they can be considered equivalent and there is no useful difference between them.
Check type always from bpf.h file you are really using. This is a bpf.h:
#ifdef MSDOS /* must be 32-bit */
typedef long bpf_int32;
typedef unsigned long bpf_u_int32;
#else
typedef int bpf_int32;
typedef u_int bpf_u_int32;
#endif

Why do C programmers use typedefs to rename basic types?

So I'm far from an expert on C, but something's been bugging me about code I've been reading for a long time: can someone explain to me why C(++) programmers use typedefs to rename simple types? I understand why you would use them for structs, but what exactly is the reason for declarations I see like
typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;
Is there some advantage to this that isn't clear to me (a programmer whose experience begins with Java and hasn't ventured far outside of strictly type-safe languages)? Because I can't think of any reason for it--it looks like it would just make the code less readable for people unfamiliar with the project.
Feel free to treat me like a C newbie, I honestly know very little about it and it's likely there are things I've misunderstood from the outset. ;)
Renaming types without changing their exposed semantics/characteristics doesn't make much sense. In your example
typedef unsigned char uch;
typedef unsigned long ulg;
belong to that category. I don't see the point, aside from making a shorter name.
But these ones
typedef uch UBYTE;
typedef unsigned int u32;
typedef signed short s16;
are a completely different story. For example, s16 stands for "signed 16 bit type". This type is not necessarily signed short. Which specific type will hide behind s16 is platform-dependent. Programmers introduce this extra level of naming indirection to simplify the support for multiple platforms. If on some other platform signed 16 bit type happens to be signed int, the programmer will only have to change one typedef definition. UBYTE apparently stands for an unsigned machine byte type, which is not necessarily unsigned char.
It's worth noting that the C99 specification already provides a standard nomenclature for integral types of specific width, like int16_t, uint32_t and so on. It probably makes more sense to stick with this standard naming convention on platforms that don't support C99.
This allows for portability. For example you need an unsigned 32-bit integer type. Which standard type is that? You don't know - it's implementation defined. That's why you typedef a separate type to be 32-bit unsigned integer and use the new type in your code. When you need to compile on another C implementation you just change the typedefs.
Sometimes it is used to reduce an unwieldy thing like volatile unsigned long to something a little more compact such as vuint32_t.
Other times it is to help with portability since types like int are not always the same on each platform. By using a typedef you can set the storage class you are interested in to the platform's closest match without changing all the source code.
There are many reasons to it. What I think is:
Typename becomes shorter and thus code also smaller and more readable.
Aliasing effect for longer structure names.
Convention used in particular team/companies/style.
Porting - Have same name across all OS and machine. Its native data-structure might be slightly different.
Following is a quote from The C Programming Language (K&R)
Besides purely aesthetic issues, there are two main reasons for using
typedefs.
First- to parameterize a program
The first is to parameterize a program against portability problems.
If typedefs are used for data types
that may be machine-dependent, only
the typedefs need change when the
program is moved.
One common situation is to use typedef names for various integer
quantities, then make an appropriate
set of choices of short, int, and long
for each host machine. Types like
size_t and ptrdiff_t from the standard library are examples.
The italicized portions tells us that programmers typedef basic type for portability. If I want to make sure my program works on different platforms, using different compiler, I will try to ensure that its portability in every possible way and typedef is one of them.
When I started programming using Turbo C compiler on Windows platform, it gave us the size of int 2. When I moved to Linux platform and GCC complier, the size I get is 4. If I had developed a program using Turbo C which relied on the assertion that sizeof( int ) is always two, it would have not ported properly to my new platform.
Hope it helps.
Following quote from K&R is not related to your query but I have posted it too for the sake of completion.
Second- to provide better documentation
The second purpose of typedefs is to provide better documentation for a
program - a type called Treeptr may be easier to understand than one declared only as a
pointer to a complicated structure.
Most of these patterns are bad practices that come from reading and copying existing bad code. Often they reflect misunderstandings about what C does or does not require.
Is akin to #define BEGIN { except it saves some typing instead of making for more.
Is akin to #define FALSE 0. If your idea of "byte" is the smallest addressable unit, char is a byte by definition. If your idea of "byte" is an octet, then either char is the octet type, or your machine has no octet type.
Is really ugly shorthand for people who can't touch type...
Is a mistake. It should be typedef uint32_t u32; or better yet, uint32_t should just be used directly.
Is the same as 4. Replace uint32_t with int16_t.
Please put a "considered harmful" stamp on them all. typedef should be used when you really need to create a new type whose definition could change over the life cycle of your code or when the code is ported to different hardware, not because you think C would be "prettier" with different type names.
We use it to make it Project/platform specific, everything has a common naming convention
pname_int32, pname_uint32, pname_uint8 -- pname is project/platform/module name
And some #defines
pname_malloc, pname_strlen
It easier to read and shortens long datatypes like unsigned char to pname_uint8 also making it a convention across all modules.
When porting you need to just modify the single file , thus making porting easy.
To cut the long story short,
you might want to do that to make your code portable (with less effort/editing).
This way you don't depend to 'int', instead you are using INTEGER that can be anything you want.
All [|u]intN_t types, where N=8|16|32|64 and so forth, are defined per architecture in this exact manner. This is a direct consequence of the fact that the standard does not mandate that char,int,float, etc. have exactly N bits - that would be insane. Instead, the standard defines minimum and maximum values of each type as guarantees to the programmer, and in various architectures types may well exceed those boundaries. It is not an uncommon sight.
The typedefs in your post are used to defined types of a certain length, in a specific architecture. It's probably not the best choice of naming; u32 and s16 are a bit too short, in my opinion. Also, it's kind of a bad thing to expose the names ulg and uch, one could prefix them with an application specific string since they obviously will not be exposed.
Hope this helps.