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 | 跨平台库 | 功能丰富 |
| Qt | GUI框架 | 完整生态 |
最佳实践
- 早期考虑跨平台需求
- 隔离平台相关代码
- 统一编码和换行符
- 持续集成测试所有平台
- 文档记录平台特定行为
跨平台开发需要细心规划和设计,但能显著扩大程序的适用范围。