Как реализовать подстановку заголовочных файлов в зависимости от платформы
Предположим, у меня есть папка root, в ней 3 папки Windows, Linux и APPLE.
В каждой из них хранится заголовочный файл hpp и файл с кодом cpp.
В root же хранится заголовочный файл thirdparty.hpp, который должен подставлять библиотеки в зависимости от платформы компиляции.
Скрин:

В каждом файле (Win32.hpp, LINUX.hpp, APPLE.hpp) содержится одинаковый набор разных функций (функции называются идентично, но работают по-разному, т.к. платформенозависимы)
Например:
WIN32.hpp:
#ifndef WIN3264TP_HPP
#define WIN3264TP_HPP
#include <windows.h>
unsigned long long GetAvailableMemory();
#endif
WIN32.cpp:
#include "../WIN32/WIN32.hpp"
unsigned long long GetAvailableMemory() {
typedef BOOL(WINAPI* PGMSE)(LPMEMORYSTATUSEX);
PGMSE pGMSE = (PGMSE)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GlobalMemoryStatusEx");
if (pGMSE){
MEMORYSTATUSEX mi;
memset(&mi, 0, sizeof(MEMORYSTATUSEX));
mi.dwLength = sizeof(MEMORYSTATUSEX);
if (pGMSE(&mi) == TRUE) return mi.ullAvailPhys;
pGMSE = 0;
}
if (!pGMSE){
MEMORYSTATUS mi;
memset(&mi, 0, sizeof(MEMORYSTATUS));
mi.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&mi);
return mi.dwAvailPhys;
}
return 0;
}
LINUX.hpp:
#ifndef LINUX_HPP
#define LINUX_HPP
#include <fstream>
#include <limits>
unsigned long long GetAvailableMemory();
#endif // LINUX_HPP
LINUX.cpp:
unsigned long long GetAvailableMemory() {
std::string token;
std::ifstream file("/proc/meminfo");
while (file >> token) {
if (token == "MemAvailable:") {
unsigned long mem;
if (file >> mem) return mem;
else return 0;
}
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return 0;
}
thirdparty.hpp же должен указывать функции из какого файла должны компилироваться, а из какого игнорироваться.
Например:
#ifndef THIRDPARTY_HPP
#define THIRDPARTY_HPP
// WINDOWS FUNCTIONS
#ifdef __WIN32
#include "WIN32/WIN32.hpp"
// APPLE FUNCTIONS
#elif __APPLE__
#include "APPLE/APPLE.hpp"
// LINUX FUNCTIONS
#elif __linux__
#include "LINUX/LINUX.hpp"
#endif
namespace ThirdParty{
unsigned long long GetAvailableMemory(){
return GetAvailableMemory();
}
}
#endif // THIRDPARTY_HPP
Вроде все составлено логично, но почему-то не компилируется...
Ошибки:

Можете объяснить - что я делаю не так?
И как можно реализовать то, о чем я написал выше?
Ответы (2 шт):
Пример выбора исходного кода для qmake
linux {
SOURCES += libs/libsuper/impl/linux.cxx
}
win32 {
SOURCES += libs/libsuper/impl/windows.cxx
}
Приведу более подробный пример с использованием cmake
Создаем только один заголовочный файл и несколько файлов реализации
$> ls ./
CMakeLists.txt impl libsuper.hxx
$> ls ./impl/
linux.cxx windows.cxx
// libsuper.hxx
#ifndef DEMO_LIBSUPER_HXX
#define DEMO_LIBSUPER_HXX
#include <cstdint>
namespace LibSuper {
uint64_t getSuperValue();
}
#endif //DEMO_LIBSUPER_HXX
// impl/linux.cxx
#include <libsuper.hxx>
namespace LibSuper
{
uint64_t getSuperValue()
{
// linux specific
return 0x42;
}
}
// impl/windows.cxx
#include <libsuper.hxx>
namespace LibSuper
{
uint64_t getSuperValue()
{
// windows specific
return 0x32;
}
}
Создадим файл проекта для cmake
# libsuper/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(super_lib LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
# добавим общие исходники
set(lib_SRC)
list(APPEND lib_SRC libsuper.hxx)
message(STATUS "System is ${CMAKE_SYSTEM_NAME}")
# определим ОС
if (WIN32)
# Windows
list(APPEND lib_SRC impl/windows.cxx)
elseif(UNIX)
# Linux
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
list(APPEND lib_SRC impl/linux.cxx)
else()
message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is not supported")
endif()
else()
message(FATAL_ERROR "Unknown system")
endif ()
# соберем нашу супер библиотеку
add_library(super ${lib_SRC})
target_include_directories(super PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Для проверки создадим проект клиента библиотеки
#include <iostream>
#include <libsuper.hxx>
int main() {
std::cout << "Hello, World!" << std::endl;
std::cout << std::hex << LibSuper::getSuperValue() << std::endl;
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(demo)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(libsuper)
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE super)
Если вы пытаетесь написать одну и ту же функцию для разных платформ, то просто поступите правильно. А точнее, берем один .h и один .cpp, при этом в заголовочном файле, допустим будет определение функции:
#ifndef GET_MEMORY_HPP
#define GET_MEMORY_HPP 1
// Здесь не нужно ничего подключать. Ну или хотя бы стандартную библиотеку <cstdint>
// #include <cstdint>
// ::uint64_t or ::size_t
unsigned long long GetAvailableMemory();
#endif
Далее, уже в файле cpp, используем макросы, и переопределение кода, который будет компилироваться, а значит:
#include "GetMemory.hpp"
#if defined WIN32 || defined _WIN32 || defined __WIN32__ || defined __NT__
# if !defined PLATFORM_WINDOWS
# define PLATFORM_WINDOWS 1
# include <Windows.h>
# endif
#elif __APPLE__
// к примеру apple не поддерживается.
# error "Sorry but Apple platform not supported now..."
#elif __linux__
# if !defined PLATFORM_LINUX
# define PLATFORM_LINUX 1
# include <fstream>
# include <limits>
# endif
#elif __unix__
# if !defined PLATFORM_UNIX
# define PLATFORM_UNIX 1
# endif
#else
# error "Unknown platform not supported..."
#endif /* PLATFORM_* */
unsigned long long GetAvailableMemory() {
unsigned long long result = 0;
#if defined PLATFORM_LINUX
std::string token;
::std::ifstream file("/proc/meminfo");
while (file >> token) {
if (token == "MemAvailable:") {
unsigned long mem;
if (file >> mem) {
result = mem;
}
}
file.ignore(::std::numeric_limits<::std::streamsize>::max(), '\n');
}
#elif defined PLATFORM_WINDOWS
typedef ::BOOL(WINAPI* PGMSE)(::LPMEMORYSTATUSEX);
PGMSE pGMSE = (PGMSE)::GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")), "GlobalMemoryStatusEx");
if (pGMSE){
::MEMORYSTATUSEX mi;
(void) ::memset(&mi, 0, sizeof(::MEMORYSTATUSEX));
mi.dwLength = sizeof(::MEMORYSTATUSEX);
if (pGMSE(&mi) == TRUE) {
result = mi.ullAvailPhys;
}
pGMSE = 0;
}
if (!pGMSE){
::MEMORYSTATUS mi;
(void) ::memset(&mi, 0, sizeof(::MEMORYSTATUS));
mi.dwLength = sizeof(::MEMORYSTATUS);
::GlobalMemoryStatus(&mi);
result = mi.dwAvailPhys;
}
#elif defined PLATFORM_MACOS
... и так далее
#else
# pragma error "Function not supported by current platform"
#endif
return result;
}