Writing cross-platform C++ Code (Windows, Linux and Mac OSX) - c++

This is my first-attempt at writing anything even slightly complicated in C++, I'm attempting to build a shared library that I can interface with from Objective-C, and .NET apps (ok, that part comes later...)
The code I have is -
#ifdef TARGET_OS_MAC
// Mac Includes Here
#endif
#ifdef __linux__
// Linux Includes Here
#error Can't be compiled on Linux yet
#endif
#ifdef _WIN32 || _WIN64
// Windows Includes Here
#error Can't be compiled on Windows yet
#endif
#include <iostream>
using namespace std;
bool probe(){
#ifdef TARGET_OS_MAC
return probe_macosx();
#endif
#ifdef __linux__
return probe_linux();
#endif
#ifdef _WIN32 || _WIN64
return probe_win();
#endif
}
bool probe_win(){
// Windows Probe Code Here
return true;
}
int main(){
return 1;
}
I have a compiler warning, simply untitled: In function ‘bool probe()’:untitled:29: warning: control reaches end of non-void function - but I'd also really appreciate any information or resources people could suggest for how to write this kind of code better....

instead of repeating yourself and writing the same #ifdef .... lines again, again, and again, you're maybe better of declaring the probe() method in a header, and providing three different source files, one for each platform. This also has the benefit that if you add a platform you do not have to modify all of your existing sources, but just add new files. Use your build system to select the appropriate source file.
Example structure:
include/probe.h
src/arch/win32/probe.cpp
src/arch/linux/probe.cpp
src/arch/mac/probe.cpp
The warning is because probe() doesn't return a value. In other words, none of the three #ifdefs matches.

I'll address this specific function:
bool probe() {
#ifdef TARGET_OS_MAC
return probe_macosx();
#elif defined __linux__
return probe_linux();
#elif defined _WIN32 || defined _WIN64
return probe_win();
#else
#error "unknown platform"
#endif
}
Writing it this way, as a chain of if-elif-else, eliminates the error because it's impossible to compile without either a valid return statement or hitting the #error.
(I believe WIN32 is defined for both 32- and 64-bit Windows, but I couldn't tell you definitively without looking it up. That would simplify the code.)
Unfortunately, you can't use #ifdef _WIN32 || _WIN64: see http://codepad.org/3PArXCxo for a sample error message. You can use the special preprocessing-only defined operator, as I did above.
Regarding splitting up platforms according to functions or entire files (as suggested), you may or may not want to do that. It's going to depend on details of your code, such as how much is shared between platforms and what you (or your team) find best to keep functionality in sync, among other issues.
Furthermore, you should handle platform selection in your build system, but this doesn't mean you can't use the preprocessor: use macros conditionally defined (by the makefile or build system) for each platform. In fact, this is the often the most practical solution with templates and inline functions, which makes it more flexible than trying to eliminate the preprocessor. It combines well with the whole-file approach, so you still use that where appropriate.
You might want to have a single config header which translates all the various compiler- and platform-specific macros into well-known and understood macros that you control. Or you could add -DBEAKS_PLAT_LINUX to your compiler command line—through your build system—to define that macro (remember to use a prefix for macro names).

It seems none of TARGET_OS_MAC, __linux__, _WIN32 or _WIN64 is defined at the time you compile your code.
So its like your code was:
bool probe(){
}
That's why the compiler complains about reaching the end of a non-void function. There is no return clause.
Also, for the more general question, here are my guidelines when developping multi-platform/architecure software/libraries:
Avoid specific cases. Try to write code that is OS-agnostic.
When dealing with system specific stuff, try to wrap things into "opaque" classes. As an example, if you are dealing with files (different APIs on Linux and Windows), try to create a File class that will embed all the logic and provide a common interface, whatever the operating system. If some feature is not available on one of the OS, deal with it: if the feature makes no sense for a specific OS, it's often fine to do nothing at all.
In short: the less #ifdef the better. And no matter how portable your code is, test it on every platform before releasing it.
Good luck ;)

The warning is because if none of the defines are actually defined then you have no return in your probe function. The fix for that is put in a default return.

To add something more to this, other than the outstanding options above, the directives __linux__ and _WIN32 are known to the compiler, where the TARGET_OS_MAC directive was not, this can be resolved by using __APPLE__. Source: http://www.winehq.org/pipermail/wine-patches/2003-July/006906.html

Related

Foolproof way to do configuration #define/#ifdef

I'm working with a moderately sized embedded C++ project that has a number of configurations for different target products. There are a good number of macros that get set for various configuration items in the different configurations and I'm trying to make that system as error-proof as possible.
Initially, I was just doing the standard thing:
#define CFG_FOO
#ifdef CFG_FOO
<code here>
#endif
but I'm always afraid I'm going to mistype a macro name in an ifdef and have a hard to find bug, because this evaluates to false without error:
#ifdef CFG_FOOO
So, I changed all the macros to this format, which requires that the macro in question be defined, defining all the ones that I want to evaluate as false to 0:
#define CFG_FOO() (1)
#define CFG_BAR() (0)
#if CFG_FOO()
<code is active>
#endif
#if CFG_BAR()
<code is inactive>
#endif
// Produces error, as desired:
#ifdef CFG_FOOO()
#endif
This was good, except that then if I accidentally enter the following (which I found I do all the time, just out of habit) it is true and the contained code is compiled:
#ifdef CFG_BAR
<this is active>
#endif
So I'm looking for a solution that:
Always generates an error for mistyped CFG_xxx item.
Doesn't allow for unintended consequences if using the wrong directive #if vs. #ifdef (it's fine if there's a error for "incorrect" usage.)
Doesn't require additional libraries/frameworks (like Boost).
Doesn't require an additional tool to process all the code (this is what I'm doing now, scanning for any #ifdef and generating an error, but that's not ideal.)
Actually removes the unneeded code. A runtime solution is probably impractical as the code size needs to be tightly controlled.
NOTE: I'm aware of the -Wundef option for gcc, but I don't believe that really helps, as the accidental #ifdef situation is still present.
My best recommendation is never get yourself into a situation where CFG_FOO is valid, CFG_BAR is valid, but both together is not valid.
We can do better by simply avoiding this problem. Specialized form for a switch ladder. CFG is a bad prefix but I'm assuming it's an artifact of minimalization and you simply will have a better one.
modeswitch.h:
#define CFGMODE_FOO 1
#define CFGMODE_BAR 2
header.h:
#if CFG == CFGMODE_FOO
#elif CFG == CFGMODE_BAR
#else
#error CFG has unsupported value
#endif
program.c
#include "modeswitch.h"
#define CFG CFGMODE_FOO
#include "header.h"
If I read this wrong and you're not using this stuff in .h files than I wonder why you have both C and C++ tags but just inline the stuff and it will still work.
My understanding is there's enough power in ## that there's a way to get rid of the pre-header but it's so hard that it doesn't meet any reasonable definition of foolproof.
Instead of repeatedly do
#if CFG_FOO()
<code is active>
#endif
since C99 or C++11, you might have once (per config)
#if CFG_FOO() // or #ifdef CFG_FOO
# define WITH_FOO(...) __VA_ARGS__
#else
# define WITH_FOO(...) /*Empty*/
#endif
And then
WITH_FOO(
<code is active>
)
Note:
It might probably break auto-indentation.
Not sure it is better than traditional way more prone to typo.
Since you're using the gcc compiler, it has a built-in #pragma for this, that throws a compiler error anytime a variable/phrase is used. This would mostly be used when you want to prevent future programmers from using things like PRINTF statements, but you can also use it in a clunkier way to eliminate the possibility that you mistype your variable names in a common way. ie. you would type something like:
#pragma GCC poison CFG_FOOO CFG_FOOD CFG_FOO0 CFG_FO
Note - I've only ever worked with this in theory, so I'm not certain it will work for you. Slightly more info in this doc:
https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html

Platform independent way to catch console/terminal close event

My question simply is:
Is it possible to catch and process console/terminal closing event platform independently?
My question isn't the same as this question, or this, or this. None of the answers on these questions provide a platform independent way. So, is there any way? If yes, then what is it? Or is it flat impossible? If so, why is that? I mean, why is it not possible to develop a library that could handle this?
EDIT: As #Yakk asked, I need it to work on both Windows and Linux, with the least code repetition possible. If it helps, I'm learning basic networking. I've built a simple chat app, in which I need to add entries in a chat history file while closing the app. I've implemented a way to close it from inside the app. But, as users are more likely to click the close button, I need to be able that event to do the operations.
There is no standard way to do so, and I am not aware of a library available, but it is not that hard to write it yourself:
#if defined(_WIN32) || defined(WIN32)
static void (*theHandler)(int);
static BOOL WINAPI winHandler(DWORD ctrlType)
{
theHandler((int)ctrlType);
return TRUE;
}
#endif
void installConsoleHandler(void (*handler)(int))
{
#if defined(_WIN32) || defined(WIN32)
theHandler = handler;
SetConsoleCtrlHandler(&winHandler, TRUE);
#else
/* sigaction, as you find in your links */
#endif
}
If you keep this code, you might extend it later for other platforms:
#if windows
#elif linux || compatible
#elif whatever
#else
#error "not implemented for current platform"
#endif
and you get a real library...
The code above is usable in C and C++ (for this I preferred static over an anonymous namespace...), so you can place it in a .c file (and compile it as C). To make it work in C++ again (as you requested), you need to tell the C++ compiler that it is a C function (meaning that there is no name mangling as in C++, which would otherwise be applied and thus the function not found during linkage), so the header would contain:
#ifdef __cplusplus
extern "C"
{
#endif
void installConsoleHandler(void (*handler)(int));
#ifdef __cplusplus
}
#endif
(Sure, you did not ask for C, but if you can get it almost for free (except for the extern "C" stuff), why not take it along? One never knows...)

Is it a bad practice to use #ifdef in code?

I have to use lot of #ifdef i386 and x86_64 for architecture specific code and some times #ifdef MAC or #ifdef WIN32... so on for platform specific code.
We have to keep the common code base and portable.
But we have to follow the guideline that use of #ifdef is strict no. I dont understand why?
As a extension to this question I would also like to understand when to use #ifdef ?
For example, dlopen() cannot open 32 bit binary while running from 64 bit process and vice versa. Thus its more architecture specific. Can we use #ifdef in such situation?
With #ifdef instead of writing portable code, you're still writing multiple pieces of platform-specific code. Unfortunately, in many (most?) cases, you quickly end up with a nearly impenetrable mixture of portable and platform-specific code.
You also frequently get #ifdef being used for purposes other than portability (defining what "version" of the code to produce, such as what level of self-diagnostics will be included). Unfortunately, the two often interact, and get intertwined. For example, somebody porting some code to MacOS decides that it needs better error reporting, which he adds -- but makes it specific to MacOS. Later, somebody else decides that the better error reporting would be awfully useful on Windows, so he enables that code by automatically #defineing MACOS if WIN32 is defined -- but then adds "just a couple more" #ifdef WIN32 to exclude some code that really is MacOS specific when Win32 is defined. Of course, we also add in the fact that MacOS is based on BSD Unix, so when MACOS is defined, it automatically defines BSD_44 as well -- but (again) turns around and excludes some BSD "stuff" when compiling for MacOS.
This quickly degenerates into code like the following example (taken from #ifdef Considered Harmful):
#ifdef SYSLOG
#ifdef BSD_42
openlog("nntpxfer", LOG_PID);
#else
openlog("nntpxfer", LOG_PID, SYSLOG);
#endif
#endif
#ifdef DBM
if (dbminit(HISTORY_FILE) < 0)
{
#ifdef SYSLOG
syslog(LOG_ERR,"couldn’t open history file: %m");
#else
perror("nntpxfer: couldn’t open history file");
#endif
exit(1);
}
#endif
#ifdef NDBM
if ((db = dbm_open(HISTORY_FILE, O_RDONLY, 0)) == NULL)
{
#ifdef SYSLOG
syslog(LOG_ERR,"couldn’t open history file: %m");
#else
perror("nntpxfer: couldn’t open history file");
#endif
exit(1);
}
#endif
if ((server = get_tcp_conn(argv[1],"nntp")) < 0)
{
#ifdef SYSLOG
syslog(LOG_ERR,"could not open socket: %m");
#else
perror("nntpxfer: could not open socket");
#endif
exit(1);
}
if ((rd_fp = fdopen(server,"r")) == (FILE *) 0){
#ifdef SYSLOG
syslog(LOG_ERR,"could not fdopen socket: %m");
#else
perror("nntpxfer: could not fdopen socket");
#endif
exit(1);
}
#ifdef SYSLOG
syslog(LOG_DEBUG,"connected to nntp server at %s", argv[1]);
#endif
#ifdef DEBUG
printf("connected to nntp server at %s\n", argv[1]);
#endif
/*
* ok, at this point we’re connected to the nntp daemon
* at the distant host.
*/
This is a fairly small example with only a few macros involved, yet reading the code is already painful. I've personally seen (and had to deal with) much worse in real code. Here the code is ugly and painful to read, but it's still fairly easy to figure out which code will be used under what circumstances. In many cases, you end up with much more complex structures.
To give a concrete example of how I'd prefer to see that written, I'd do something like this:
if (!open_history(HISTORY_FILE)) {
logerr(LOG_ERR, "couldn't open history file");
exit(1);
}
if ((server = get_nntp_connection(server)) == NULL) {
logerr(LOG_ERR, "couldn't open socket");
exit(1);
}
logerr(LOG_DEBUG, "connected to server %s", argv[1]);
In such a case, it's possible that our definition of logerr would be a macro instead of an actual function. It might be sufficiently trivial that it would make sense to have a header with something like:
#ifdef SYSLOG
#define logerr(level, msg, ...) /* ... */
#else
enum {LOG_DEBUG, LOG_ERR};
#define logerr(level, msg, ...) /* ... */
#endif
[for the moment, assuming a preprocessor that can/will handle variadic macros]
Given your supervisor's attitude, even that may not be acceptable. If so, that's fine. Instead a macro, implement that capability in a function instead. Isolate each implementation of the function(s) in its own source file and build the files appropriate to the target. If you have a lot of platform-specific code, you usually want to isolate it into a directory of its own, quite possibly with its own makefile1, and have a top-level makefile that just picks which other makefiles to invoke based on the specified target.
Some people prefer not to do this. I'm not really arguing one way or the other about how to structure makefiles, just noting that it's a possibility some people find/consider useful.
You should avoid #ifdef whenever possible. IIRC, it was Scott Meyers who wrote that with #ifdefs you do not get platform-independent code. Instead you get code that depends on multiple platforms. Also #define and #ifdef are not part of the language itself. #defines have no notion of scope, which can cause all sorts of problems. The best way is to keep the use of the preprocessor to a bare minimum, such as the include guards. Otherwise you are likely to end up with a tangled mess, which is very hard to understand, maintain, and debug.
Ideally, if you need to have platform-specific declarations, you should have separate platform-specific include directories, and handle them appropriately in your build environment.
If you have platform specific implementation of certain functions, you should also put them into separate .cpp files and again hash them out in the build configuration.
Another possibility is to use templates. You can represent your platforms with empty dummy structs, and use those as template parameters. Then you can use template specialization for platform-specific code. This way you would be relying on the compiler to generate platform-specific code from templates.
Of course, the only way for any of this to work, is to very cleanly factor out platform-specific code into separate functions or classes.
I have seen 3 broad usages of #ifdef:
isolate platform specific code
isolate feature specific code (not all versions of a compilers / dialect of a language are born equal)
isolate compilation mode code (NDEBUG anyone ?)
Each has the potential to create a huge mess of unmaintanable code, and should be treated accordingly, but not all of them can be dealt with in the same fashion.
1. Platform specific code
Each platform comes with its own set of specific includes, structures and functions to deal with things like IO (mainly).
In this situation, the simplest way to deal with this mess is to present a unified front, and have platform specific implementations.
Ideally:
project/
include/namespace/
generic.h
src/
unix/
generic.cpp
windows/
generic.cpp
This way, the platform stuff is all kept together in one single file (per header) so easy to locate. The generic.h file describes the interface, the generic.cpp is selected by the build system. No #ifdef.
If you want inline functions (for performance), then a specific genericImpl.i providing the inline definitions and platform specific can be included at the end of the generic.h file with a single #ifdef.
2. Feature specific code
This gets a bit more complicated, but is usually experienced only by libraries.
For example, Boost.MPL is much easier to implement with compilers having variadic templates.
Or, compilers supporting move constructors allow you to define more efficient versions of some operations.
There is no paradise here. If you find yourself in such a situation... you end up with a Boost-like file (aye).
3. Compilation Mode code
You can generally get away with a couple #ifdef. The traditional example is assert:
#ifdef NDEBUG
# define assert(X) (void)(0)
#else // NDEBUG
# define assert(X) do { if (!(X)) { assert_impl(__FILE__, __LINE__, #X); } while(0)
#endif // NDEBUG
Then, the use of the macro itself is not susceptible to the compilation mode, so at least the mess is contained within a single file.
Beware: there is a trap here, if the macro is not expanded to something that counts for a statement when "ifdefed away" then you risk to change the flow under some circumstances. Also, macro not evaluating their arguments may lead to strange behavior when there are function calls (with side effects) in the mix, but in this case this is desirable as the computation involved may be expensive.
Many programs use such a scheme to make platform specific code. A better way, and also a way to clean up the code, is to put all code specific to one platform in one file, naming the functions the same and having the same arguments. Then you just select which file to build depending on the platform.
It might still be some places left where you can not extract platform specific code into separate functions or files, and you still might need the #ifdef parts, but hopefully it should be minimized.
I prefer splitting the platform dependent code & features into separate translation units and letting the build process decide which units to use.
I've lost a week of debugging time due to misspelled identifiers. The compiler does not do checking of defined constants across translation units. For example, one unit may use "WIN386" and another "WIN_386". Platform macros are a maintenance nightmare.
Also, when reading the code, you have to check the build instructions and header files to see which identifers are defined. There is also a difference between an identifier existing and having a value. Some code may test for the existance of an identifier while another tests the value of the same identifer. The latter test is undefined when the identifier is not specified.
Just believe they are evil and prefer not to use them.
Not sure what you mean by "#ifdef is strict no", but perhaps you are referring to a policy on a project you are working on.
You might consider not checking for things like Mac or WIN32 or i386, though. In general, you do not actually care if you are on a Mac. Instead, there is some feature of MacOS that you want, and what you care about is the presence (or absence) of that feature. For that reason, it is common to have a script in your build setup that checks for features and #defines things based on the features provided by the system, rather than making assumptions about the presence of features based on the platform. After all, you might assume certain features are absent on MacOS, but someone may have a version of MacOS on which they have ported that feature. The script that checks for such features is commonly called "configure", and it is often generated by autoconf.
personally, I prefer to abstract that noise well (where necessary). if it's all over the body of a class' interface - yuck!
so, let's say there is a type which is platform defined:
I will use a typedef at a high level for the inner bits and create an abstraction - that's often one line per #ifdef/#else/#endif.
then for the implementation, i will also use a single #ifdef for that abstraction in most cases (but that does mean that the platform specific definitions appear once per platform). I also separate them into separate platform specific files so I can rebuild a project by throwing all the sources into a project and building without a hiccup. In that case, #ifdef is also handier than trying to figure out all the dependencies per project per platform, per build type.
So, just use it to focus on the platform specific abstraction you need, and use abstractions so the client code is the same -- just like reducing the scope of a variable ;)
Others have indicated the preferred solution: put the dependent code in
a separate file, which is included. This the files corresponding to
different implementations can either be in separate directories (one of
which is specified by means of a -I or a /I directive in the
invocation), or by building up the name of the file dynamically (using
e.g macro concatenation), and using something like:
#include XX_dependentInclude(config.hh)
(In this case, XX_dependentInclude might be defined as something like:
#define XX_string2( s ) # s
#define XX_stringize( s ) XX_string2(s)
#define XX_paste2( a, b ) a ## b
#define XX_paste( a, b ) XX_paste2( a, b )
#define XX_dependentInclude(name) XX_stringize(XX_paste(XX_SYST_ID,name))
and SYST_ID is initialized using -D or /D in the compiler
invocation.)
In all of the above, replace XX_ with the prefix you usually use for macros.

Does Visual Studio have a namespace-sensitive macro-substitution option?

What I'm talking about is to find a way to avoid the macros in <windows.h> from polluting whatever project I'm writing.
Excerpts from windows.h:
#ifdef UNICODE
#define LoadImage LoadImageW
#else
#define LoadImage LoadImageA
#endif // !UNICODE
#ifdef UNICODE
#define GetMessage GetMessageW
#else
#define GetMessage GetMessageA
#endif // !UNICODE
The majority of macros (over 99%) I'm okay with, but some of them I just couldn't find a way to avoid.
My idea is that since I always qualify the functions calls in my particular framework, e.g. ImageTool::LoadImage, Visual Studio should have enough clue that I'm not referencing the Windows API, which are all in the root namespace, i.e. ::LoadImage. But the MACRO system does not seem to be that smart.
Is there a compiler or preprocessor option that will just enable that?
Macro substitution are basically simple textual replacements, done before the proper compiler even starts to parse the code. Therefore they are not aware of namespaces or any other parts of the C++ syntax above the pure lexical level.
The straight forward way to avoid replacement of your identifiers is to remove the macros:
#ifdef LoadImage
#undef LoadImage
#endif
This of course will also stop following code from accessing the Windows API with the name LoadImage.
You will to live with it - you cannot avoid these macros on Windows platform. There is no macro-name-spacing in C/C++ pre-processor world. You may however, have all of your code defined and implemented before including any windows header - but that's wouldn't be possible, I believe.

How to make C++ program work across compilers

I wanted to know how I would make my C++ program work across compilers. I wanted to make the program so if it's being compiled with borland it will use the clrscr() function otherwise it'd use system("CLS"). I've seen code that has done something similar but I couldn't find an explanation of what it does or how it works. Any help would be appreciated.
In general, to make a C or C++ program work across multiple compilers you want to confine yourself to standard C or C++ as much as possible. Sometimes you have to use compiler/platform specific functionality, though, and one way to handle that is via the preprocessor.
The predef project on SourceForge lists a bunch a preprocessor symbols that are defined automatically by various compilers, for various platforms, et cetera. You can use that information to implement what you need, for example:
void clearScreen() {
// __BORLANDC__ is defined by the Borland C++ compiler.
#ifdef __BORLANDC__
clrscr();
#else
system("cls");
#endif
}
One easy answer from the top of the head is define your own function calls and then translate it into real calls depending on the compiling parameters (with #ifdef preprocessing definitions - look which values are corresponding to which compiler).
example:
#if defined(__COMPILER_ONE__)
#define ClearScreen() clrscr()
#elif defined(__COMPILER_TWO__)
#define ClearScreen() system("CLS")
#else
#error "I do not know what to do!"
#endif
You would have to create a dedicated header file for this and to include it everywhere, of course.
(Of course you have to substitute COMPILER_ONE and COMPILER_TWO with relevant definitions :) )
How to make something work across different compilers is simple question which is very complex to answer! Your specific query about clearing the screen;
I would attempt it like this, first you have your own function say
void clear_screen();
And define it like this:
void clear_screen()
{
#ifdef LINUX
...
#eleif MS_WIN
...
#endif
}
Please note I have just guessed what the #define 's are. This is know as conditional complication, generally regarded as evil, but containing it in a function reduces the harm a little.
The way it's typically done is through the magic of the preprocessor or makefiles. Either way, you hide the implementation details behind a common interface in a header file, such as void clearscreen(). Then in a single source file you can hide the Borland implementation behind #ifdef BORLAND, and similarly for other implementations. Alternatively, you can put each implementation in a separate source file, and only compile the proper one based on a variable in a makefile.
You can do this by checking compiler macros with the #ifdef compiler macro:
#ifdef BORLAND
borland();
#else
otherCompiler();
#endif