Detect Windows or Linux in C, C++

103,547

Solution 1

It's generally done like this (more or less):

#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define DIV 1048576 
#define WIDTH 7
#endif

#ifdef linux
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#endif


int main(int argc, char *argv[]) 
{
#ifdef _WIN32
MEMORYSTATUSEX statex;
    statex.dwLength = sizeof (statex);
    GlobalMemoryStatusEx (&statex);

    _tprintf (TEXT("There is  %*ld %% of memory in use.\n"),
            WIDTH, statex.dwMemoryLoad);
#endif

#ifdef linux
char cmd[30];
int flag = 0;   
FILE *fp;
char line[130];     
int TotalMem, TotalFree, TotalUsed;

flag=0;
memcpy (cmd,"\0",30);
sprintf(cmd,"free -t -m|grep Total");          
fp = popen(cmd, "r");       
while ( fgets( line, sizeof line, fp))
{   
    flag++;
    sscanf(line,"%*s %d %d %d",&TotalMem, &TotalUsed, &TotalFree);
}
pclose(fp); 

if(flag)
    printf("TotalMem:%d -- TotalUsed:%d -- TotalFree:%d\n",TotalMem,TotalUsed,TotalFree);
else 
    printf("not found\n");
#endif

    return 0;
}

This way, only code for linux will be compiled while on a linux platform, and only windows code will be compiled on a windows platform.

Solution 2

You should use the same #ifdef instead of if(OS_Windows) logic in your code:

#ifdef __unix__         
...
#elif defined(_WIN32) || defined(WIN32) 

#define OS_Windows

#endif

int main(int argc, char *argv[]) 
{
#ifdef OS_Windows
 /* Windows code */
#else
 /* GNU/Linux code */
#endif    
}

Solution 3

I see a lot of varying solutions here, which makes me uncomfortable... What if they work on Linux but not Windows or on Windows but not Linux? What if they only work on some compilers? Etc.

So I found this link, which I like: https://web.archive.org/web/20191012035921/http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system

It looks like these are best (using #ifdef, #endif, etc.):

  • _WIN32 for Windows 32 bit OR 64 bit
  • _WIN64 for Windows 64 bit only
  • __unix__ for Unix

Solution 4

You are confusing variables (which exist in run-time) with preprocessor symbols (which only exist during compilation).

After you do something like #define OS_Windows 1, you can't use the symbol OS_Windows as a variable and put it inside if()s... I mean, you can, but it will be expanded during compilation to if (1).

For a cross-platform project, you have to use #if or #ifdef to make sure that the compiler chooses a correct part of code for a given OS and compiles only that.

Something like:

void openWindow() {
#if OS_Windows
    // windows-specific code goes here
#else
    // linux-specific code
#endif
}

Solution 5

Your basic strategy is deeply flawed.

By using the if in the main function you are selecting which chunk of code to run at RUNTIME. Thus even if compiling for unix the compiler still has to build the code for windows (which it is failing to do because the header files are not included) and visa versa.

You instead required #if which will be evaluated at compile time and will not attempt to compile irrelevant code.

So in essence you need to understand that if evaluates your define as an expression at runtime whereas #if evaluates the value of the predefined constant at compile time.

Share:
103,547
Ronin
Author by

Ronin

Updated on December 16, 2020

Comments

  • Ronin
    Ronin over 3 years

    I am writing a cross platform program. I want this one program to run under both Windows and Linux, so I have two different code segments for the two platforms. If the OS is Windows, I want the first code segment to run; if it's Linux, then I want the second code segment to run.

    So I wrote the following code, but it gets an error while building both on Windows and on Linux. What should I do to solve it?

    #ifdef __unix__                    /* __unix__ is usually defined by compilers targeting Unix systems */
    
        #define OS_Windows 0
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <string.h>
    
    #elif defined(_WIN32) || defined(WIN32)     /* _Win32 is usually defined by compilers targeting 32 or   64 bit Windows systems */
    
        #define OS_Windows 1
        #include <windows.h>
        #include <stdio.h>
        #include <tchar.h>
        #define DIV 1048576
        #define WIDTH 7
    
    #endif
    
    int main(int argc, char *argv[])
    {
        if(OS_Windows)
        {
            MEMORYSTATUSEX statex;
            statex.dwLength = sizeof (statex);
            GlobalMemoryStatusEx (&statex);
    
            _tprintf (TEXT("There is  %*ld %% of memory in use.\n"),
                        WIDTH, statex.dwMemoryLoad);
    
        }
    
        else if(!OS_Windows) // if OS is unix
    
        {
            char cmd[30];
            int flag = 0;
            FILE *fp;
            char line[130];
            int memTotal, memFree, memUsed;
    
            flag=0;
            memcpy (cmd,"\0",30);
            sprintf(cmd,"free -t -m|grep Total");
            fp = popen(cmd, "r");
            while ( fgets( line, sizeof line, fp))
            {
                flag++;
                sscanf(line,"%*s %d %d %d",&TotalMem, &TotalUsed, &TotalFree);
            }
            pclose(fp);
    
            if(flag)
                printf("TotalMem:%d -- TotalUsed:%d -- TotalFree:%d\n",TotalMem,TotalUsed,TotalFree);
            else
                printf("not found\n");
    
        }
    
        return 0;
    }
    
    • Zaur Nasibov
      Zaur Nasibov over 12 years
      What kind of error?
    • Ronin
      Ronin over 12 years
      on linux its getting error :: âMEMORYSTATUSEXâ was not declared in this scope, âstatexâ was not declared in this scope and in windows :: error C3861: 'popen': identifier not found ... similar like this.... whay to do ?
    • Zaur Nasibov
      Zaur Nasibov over 12 years
      This happens because compiler is looking for MEMORYSTATUSEX, because if(OS_Windows) check is NOT preprocessor check, it happens at run-time, thus everything that comes under that if must be compiled.
    • Kos
      Kos over 12 years
      @RRR please remember to check the tick mark next to the answer which best solved your problem, that's how we mark questions as solved on SO. Please also do this with your other questions :-)
    • Basile Starynkevitch
      Basile Starynkevitch over 12 years
      There exist cross-platform C++ libraries, like Qt. With its QtCore module, you could code a non-graphical application portably between Linux, MacOSX, Windows.
    • Dave
      Dave over 12 years
      @RRR I guarantee that memcpy(cmd, "\0", 30) doesn't do what you expect. Use memset(cmd, '\0', 30)
    • phuclv
      phuclv almost 5 years
  • Dave
    Dave over 12 years
    Any optimizing compiler worth its salt will remove an if(0) block.
  • Ronin
    Ronin over 12 years
    thankzzz.... u r a life-saver.
  • Elemental
    Elemental over 12 years
    @Dave Unfortunately that doesn't really help because the compiler can't optimise anything it won't compile to start with
  • Jeegar Patel
    Jeegar Patel over 12 years
    @Cicada to make compile this code change int memTotal, memFree, memUsed; to int TotalMem, TotalFree, TotalUsed;
  • Dave
    Dave over 12 years
    What I mean is that the problem isn't extraneous symbols; it's non-existent ones.
  • user1234567
    user1234567 over 7 years
    >> "it will be expanded during compilation to if (1)" Yes, or to if (0) if OS_Windows was defined to 0. The main question is why to postpone this branching to run-time, if we in compile-time already know, which branch we need. So by #if-constructions we could avoid extra run-time & extra code
  • Jack G
    Jack G over 6 years
    @Dave Likewise, any compiler worth its salt will also not remove if(0) when -O0.
  • xmoex
    xmoex over 3 years
    the link is dead... consider providing an alternative?
  • Andrew
    Andrew over 3 years
    @xmoex That's a recent development. I'll update my answer with an archived link.
  • Andrew
    Andrew over 3 years
    @xmoex The gist is that it lists several dozen various flags used by this or that or the other OS and this or that or the other compiler, and these 3 above appear to be the best in terms of overlap for determining Windows vs. Linux. Bonus: For Apple/Mac, both __APPLE__ and __MACH__ (not a typo) seem to work universally.
  • mercury
    mercury about 2 years
    Cygwin is still windows but it does not detect my system as windows.
  • phetdam
    phetdam about 2 years
    @Andrew here's the official Microsoft documentation on predefined macros. note that (quoting directly): "_WIN32 Defined as 1 when the compilation target is 32-bit ARM, 64-bit ARM, x86, or x64. Otherwise, undefined" and "_WIN64 Defined as 1 when the compilation target is 64-bit ARM or x64. Otherwise, undefined". so assuming Microsoft didn't mess up its own documentation, if you just want to test for "Windows" then #ifdef _WIN32 should be enough.
  • Andrew
    Andrew about 2 years
    @phetdam You are correct. I clarified in my answer. Based on my link it also appears to hold true for other major compilers too.
  • phetdam
    phetdam about 2 years
    @Andrew awesome :)