Skip to content

C++ 跨平台开发

概述

跨平台开发允许C++程序在不同操作系统上运行。本章介绍跨平台开发的策略、工具和最佳实践。

🌍 平台差异处理

条件编译

cpp
#include <iostream>

// 平台检测宏
#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系统,处理器数量: " + std::to_string(sysInfo.dwNumberOfProcessors);
        #else
            struct utsname unameData;
            uname(&unameData);
            return std::string(unameData.sysname) + " " + unameData.release;
        #endif
    }
};

// 文件路径处理
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
            // 将'/'替换为'\'
            for (char& c : result) {
                if (c == '/') c = '\\';
            }
        #endif
        return result;
    }
};

🛠️ 构建系统

CMake示例

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

# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 平台特定设置
if(WIN32)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
elseif(UNIX)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
endif()

# 源文件
set(SOURCES
    src/main.cpp
    src/platform_utils.cpp
)

# 平台特定源文件
if(WIN32)
    list(APPEND SOURCES src/windows_specific.cpp)
elseif(UNIX)
    list(APPEND SOURCES src/unix_specific.cpp)
endif()

# 可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})

# 链接库
if(WIN32)
    target_link_libraries(${PROJECT_NAME} ws2_32)
elseif(UNIX)
    target_link_libraries(${PROJECT_NAME} pthread)
endif()

# 安装规则
install(TARGETS ${PROJECT_NAME} DESTINATION bin)

跨平台库集成

cpp
// 使用Boost库实现跨平台功能
#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 << "当前目录: " << currentPath.string() << std::endl;
        
        // 跨平台路径操作
        fs::path configFile = currentPath / "config" / "app.conf";
        std::cout << "配置文件路径: " << configFile.string() << std::endl;
        
        // 目录遍历
        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 << "线程 " << i << " 运行在: " 
                          << PlatformUtils::getPlatformName() << std::endl;
                boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
            });
        }
        
        threads.join_all();
    }
};

📱 平台抽象层

统一接口设计

cpp
// 抽象接口
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实现
#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实现
#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

// 工厂函数
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
}

🔧 编译配置

编译脚本示例

bash
#!/bin/bash
# build.sh - 跨平台编译脚本

# 检测平台
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
    PLATFORM="linux"
elif [[ "$OSTYPE" == "darwin"* ]]; then
    PLATFORM="macos"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
    PLATFORM="windows"
else
    echo "不支持的平台: $OSTYPE"
    exit 1
fi

echo "构建平台: $PLATFORM"

# 创建构建目录
mkdir -p build
cd build

# 运行CMake
cmake .. -DCMAKE_BUILD_TYPE=Release

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

echo "构建完成"
bat
@echo off
REM build.bat - Windows编译脚本

echo 构建平台: Windows

if not exist build mkdir build
cd build

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

echo 构建完成
pause

📋 配置管理

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 << "=== 跨平台开发演示 ===" << std::endl;
    
    std::cout << "平台: " << PlatformUtils::getPlatformName() << std::endl;
    std::cout << "系统信息: " << PlatformUtils::getSystemInfo() << std::endl;
    
    // 文件系统演示
    auto fs = createFileSystem();
    if (fs) {
        std::cout << "当前目录文件:" << std::endl;
        auto files = fs->listDirectory(".");
        for (const auto& file : files) {
            std::cout << "  " << file << std::endl;
        }
    }
    
    // 配置管理
    ConfigManager config;
    config.loadConfig();
    std::cout << "应用名称: " << config.getValue("app_name") << std::endl;
    
    return 0;
}

总结

跨平台策略

  • 条件编译: 使用预处理器宏处理平台差异
  • 抽象层: 设计统一接口隐藏平台细节
  • 标准库优先: 优先使用C++标准库
  • 第三方库: 使用成熟的跨平台库

工具选择

工具用途优势
CMake构建系统广泛支持
Boost跨平台库功能丰富
QtGUI框架完整生态

最佳实践

  • 早期考虑跨平台需求
  • 隔离平台相关代码
  • 统一编码和换行符
  • 持续集成测试所有平台
  • 文档记录平台特定行为

跨平台开发需要细心规划和设计,但能显著扩大程序的适用范围。

本站内容仅供学习和研究使用。