Skip to content

C++ Cross-Platform Development

Overview

Cross-platform development allows C++ programs to run on different operating systems. This chapter introduces cross-platform development strategies, tools, and best practices.

🌍 Platform Difference Handling

Conditional Compilation

cpp
#include <iostream>

// Platform detection macros
#ifdef _WIN32
    #include <windows.h>
    #define PLATFORM_WINDOWS
#elif __linux__
    #include <unistd.h>
    #include <sys/utsname.h>
    #define PLATFORM_LINUX
#elif __APPLE__
    #include <sys/utsname.h>
    #define PLATFORM_MACOS
#endif

class PlatformUtils {
public:
    static std::string getPlatformName() {
        #ifdef PLATFORM_WINDOWS
            return "Windows";
        #elif PLATFORM_LINUX
            return "Linux";
        #elif PLATFORM_MACOS
            return "macOS";
        #else
            return "Unknown";
        #endif
    }
    
    static void sleep(int milliseconds) {
        #ifdef PLATFORM_WINDOWS
            Sleep(milliseconds);
        #else
            usleep(milliseconds * 1000);
        #endif
    }
    
    static std::string getSystemInfo() {
        #ifdef PLATFORM_WINDOWS
            SYSTEM_INFO sysInfo;
            GetSystemInfo(&sysInfo);
            return "Windows system, processor count: " + std::to_string(sysInfo.dwNumberOfProcessors);
        #else
            struct utsname unameData;
            uname(&unameData);
            return std::string(unameData.sysname) + " " + unameData.release;
        #endif
    }
};

// File path handling
class PathUtils {
public:
    static const char PATH_SEPARATOR =
        #ifdef PLATFORM_WINDOWS
            '\\';
        #else
            '/';
        #endif
    
    static std::string joinPath(const std::string& dir, const std::string& file) {
        return dir + PATH_SEPARATOR + file;
    }
    
    static std::string normalizePath(const std::string& path) {
        std::string result = path;
        #ifdef PLATFORM_WINDOWS
            // Replace '/' with '\'
            for (char& c : result) {
                if (c == '/') c = '\\';
            }
        #endif
        return result;
    }
};

🛠️ Build Systems

CMake Example

cmake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(CrossPlatformApp)

# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Platform-specific settings
if(WIN32)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
elseif(UNIX)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
endif()

# Source files
set(SOURCES
    src/main.cpp
    src/platform_utils.cpp
)

# Platform-specific source files
if(WIN32)
    list(APPEND SOURCES src/windows_specific.cpp)
elseif(UNIX)
    list(APPEND SOURCES src/unix_specific.cpp)
endif()

# Executable
add_executable(${PROJECT_NAME} ${SOURCES})

# Link libraries
if(WIN32)
    target_link_libraries(${PROJECT_NAME} ws2_32)
elseif(UNIX)
    target_link_libraries(${PROJECT_NAME} pthread)
endif()

# Installation rules
install(TARGETS ${PROJECT_NAME} DESTINATION bin)

Cross-Platform Library Integration

cpp
// Use Boost library for cross-platform functionality
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>

class CrossPlatformApp {
public:
    static void demonstrateFilesystem() {
        namespace fs = boost::filesystem;
        
        fs::path currentPath = fs::current_path();
        std::cout << "Current directory: " << currentPath.string() << std::endl;
        
        // Cross-platform path operations
        fs::path configFile = currentPath / "config" / "app.conf";
        std::cout << "Config file path: " << configFile.string() << std::endl;
        
        // Directory traversal
        if (fs::exists(currentPath) && fs::is_directory(currentPath)) {
            for (const auto& entry : fs::directory_iterator(currentPath)) {
                std::cout << entry.path().filename().string() << std::endl;
            }
        }
    }
    
    static void demonstrateThreading() {
        boost::thread_group threads;
        
        for (int i = 0; i < 4; ++i) {
            threads.create_thread([i]() {
                std::cout << "Thread " << i << " running on: " 
                          << PlatformUtils::getPlatformName() << std::endl;
                boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
            });
        }
        
        threads.join_all();
    }
};

📱 Platform Abstraction Layer

Unified Interface Design

cpp
// Abstract interface
class IFileSystem {
public:
    virtual ~IFileSystem() = default;
    virtual bool fileExists(const std::string& path) = 0;
    virtual std::string readFile(const std::string& path) = 0;
    virtual bool writeFile(const std::string& path, const std::string& content) = 0;
    virtual std::vector<std::string> listDirectory(const std::string& path) = 0;
};

// Windows implementation
#ifdef PLATFORM_WINDOWS
class WindowsFileSystem : public IFileSystem {
public:
    bool fileExists(const std::string& path) override {
        return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES;
    }
    
    std::string readFile(const std::string& path) override {
        std::ifstream file(path);
        return std::string((std::istreambuf_iterator<char>(file)),
                          std::istreambuf_iterator<char>());
    }
    
    bool writeFile(const std::string& path, const std::string& content) override {
        std::ofstream file(path);
        if (file.is_open()) {
            file << content;
            return true;
        }
        return false;
    }
    
    std::vector<std::string> listDirectory(const std::string& path) override {
        std::vector<std::string> files;
        WIN32_FIND_DATAA findData;
        HANDLE hFind = FindFirstFileA((path + "\\*").c_str(), &findData);
        
        if (hFind != INVALID_HANDLE_VALUE) {
            do {
                if (strcmp(findData.cFileName, ".") != 0 && 
                    strcmp(findData.cFileName, "..") != 0) {
                    files.push_back(findData.cFileName);
                }
            } while (FindNextFileA(hFind, &findData));
            FindClose(hFind);
        }
        
        return files;
    }
};
#endif

// Unix implementation
#ifdef PLATFORM_LINUX
class UnixFileSystem : public IFileSystem {
public:
    bool fileExists(const std::string& path) override {
        return access(path.c_str(), F_OK) == 0;
    }
    
    std::string readFile(const std::string& path) override {
        std::ifstream file(path);
        return std::string((std::istreambuf_iterator<char>(file)),
                          std::istreambuf_iterator<char>());
    }
    
    bool writeFile(const std::string& path, const std::string& content) override {
        std::ofstream file(path);
        if (file.is_open()) {
            file << content;
            return true;
        }
        return false;
    }
    
    std::vector<std::string> listDirectory(const std::string& path) override {
        std::vector<std::string> files;
        DIR* dir = opendir(path.c_str());
        
        if (dir) {
            struct dirent* entry;
            while ((entry = readdir(dir)) != nullptr) {
                if (strcmp(entry->d_name, ".") != 0 && 
                    strcmp(entry->d_name, "..") != 0) {
                    files.push_back(entry->d_name);
                }
            }
            closedir(dir);
        }
        
        return files;
    }
};
#endif

// Factory function
std::unique_ptr<IFileSystem> createFileSystem() {
    #ifdef PLATFORM_WINDOWS
        return std::make_unique<WindowsFileSystem>();
    #elif PLATFORM_LINUX
        return std::make_unique<UnixFileSystem>();
    #else
        return nullptr;
    #endif
}

🔧 Compilation Configuration

Compilation Script Examples

bash
#!/bin/bash
# build.sh - Cross-platform compilation script

# Detect platform
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
    PLATFORM="linux"
elif [[ "$OSTYPE" == "darwin"* ]]; then
    PLATFORM="macos"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
    PLATFORM="windows"
else
    echo "Unsupported platform: $OSTYPE"
    exit 1
fi

echo "Building for platform: $PLATFORM"

# Create build directory
mkdir -p build
cd build

# Run CMake
cmake .. -DCMAKE_BUILD_TYPE=Release

# Compile
if [[ "$PLATFORM" == "windows" ]]; then
    cmake --build . --config Release
else
    make -j$(nproc)
fi

echo "Build complete"
bat
@echo off
REM build.bat - Windows compilation script

echo Building for platform: Windows

if not exist build mkdir build
cd build

cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release

echo Build complete
pause

📋 Configuration Management

cpp
#include <iostream>
#include <map>
#include <fstream>

class ConfigManager {
private:
    std::map<std::string, std::string> config_;
    
public:
    bool loadConfig() {
        std::string configFile = getConfigPath();
        std::ifstream file(configFile);
        
        if (!file.is_open()) {
            createDefaultConfig();
            return false;
        }
        
        std::string line;
        while (std::getline(file, line)) {
            size_t pos = line.find('=');
            if (pos != std::string::npos) {
                std::string key = line.substr(0, pos);
                std::string value = line.substr(pos + 1);
                config_[key] = value;
            }
        }
        
        return true;
    }
    
    std::string getValue(const std::string& key, const std::string& defaultValue = "") {
        auto it = config_.find(key);
        return (it != config_.end()) ? it->second : defaultValue;
    }
    
private:
    std::string getConfigPath() {
        #ifdef PLATFORM_WINDOWS
            return "C:\\ProgramData\\MyApp\\config.ini";
        #elif PLATFORM_MACOS
            return "/Users/" + std::string(getenv("USER")) + "/Library/Application Support/MyApp/config.ini";
        #else
            return "/home/" + std::string(getenv("USER")) + "/.myapp/config.ini";
        #endif
    }
    
    void createDefaultConfig() {
        config_["app_name"] = "MyApp";
        config_["version"] = "1.0.0";
        config_["log_level"] = "INFO";
        
        #ifdef PLATFORM_WINDOWS
            config_["temp_dir"] = "C:\\Temp";
        #else
            config_["temp_dir"] = "/tmp";
        #endif
    }
};

int main() {
    std::cout << "=== Cross-Platform Development Demo ===" << std::endl;
    
    std::cout << "Platform: " << PlatformUtils::getPlatformName() << std::endl;
    std::cout << "System info: " << PlatformUtils::getSystemInfo() << std::endl;
    
    // Filesystem demo
    auto fs = createFileSystem();
    if (fs) {
        std::cout << "Current directory files:" << std::endl;
        auto files = fs->listDirectory(".");
        for (const auto& file : files) {
            std::cout << "  " << file << std::endl;
        }
    }
    
    // Configuration management
    ConfigManager config;
    config.loadConfig();
    std::cout << "Application name: " << config.getValue("app_name") << std::endl;
    
    return 0;
}

Summary

Cross-Platform Strategies

  • Conditional Compilation: Use preprocessor macros to handle platform differences
  • Abstraction Layer: Design unified interfaces to hide platform details
  • Standard Library First: Prioritize C++ standard library
  • Third-Party Libraries: Use mature cross-platform libraries

Tool Selection

ToolPurposeAdvantage
CMakeBuild systemWidely supported
BoostCross-platform libraryFeature-rich
QtGUI frameworkComplete ecosystem

Best Practices

  • Consider cross-platform requirements early
  • Isolate platform-related code
  • Unify encoding and line endings
  • Continuous integration testing on all platforms
  • Document platform-specific behaviors

Cross-platform development requires careful planning and design but can significantly expand the applicability of your programs.

Content is for learning and research only.