libostd/ostd/platform.hh

364 lines
9.4 KiB
C++
Raw Permalink Normal View History

2017-03-27 18:51:50 +02:00
/** @defgroup Platform Platform support
*
2017-03-27 18:51:50 +02:00
* @brief Abstractions for platform (OS, toolchain) specific code.
*
2017-04-06 20:14:52 +02:00
* libostd is not only a simple utility library, it also aims to make writing
2017-03-27 18:51:50 +02:00
* cross-platform code as simple as possible (while sticking to native features
* and therefore not making your code feel foreign on the platform). This
* module represents the base layer to achieve this.
*
* @{
*/
/** @file platform.hh
*
* @brief Platform specific definitions.
*
* This file defines various helper macros to deal with system and compiler
* checks as well as API exports and byte order.
*
* @copyright See COPYING.md in the project tree for further information.
*/
2015-07-13 21:08:55 +02:00
#ifndef OSTD_PLATFORM_HH
#define OSTD_PLATFORM_HH
2017-04-09 16:44:45 +02:00
#include <cstddef>
2017-01-30 19:19:09 +01:00
#include <cstdint>
2015-06-27 03:05:25 +02:00
2017-02-08 01:06:50 +01:00
#include <type_traits>
2017-03-27 18:51:50 +02:00
/* only expanded when generating documentation, define all macros */
#ifdef OSTD_GENERATING_DOC
/// Defined on Windows x86 and x86_64, otherwise undefined.
#define OSTD_PLATFORM_WIN32
/// Defined on Windows x64 only, otherwise undefined.
#define OSTD_PLATFORM_WIN64
/// Defined on all POSIX compliant systems, otherwise undefined.
#define OSTD_PLATFORM_POSIX
/// Defined on Linux.
#define OSTD_PLATFORM_LINUX
/// Defined on macOS.
#define OSTD_PLATFORM_OSX
/// Defined on DragonflyBSD.
#define OSTD_PLATFORM_DRAGONFLYBSD
/// Defined on FreeBSD.
#define OSTD_PLATFORM_FREEBSD
/// Defined on OpenBSD.
#define OSTD_PLATFORM_OPENBSD
/// Defined on NetBSD.
#define OSTD_PLATFORM_NETBSD
/// Defined on FreeBSD, NetBSD, OpenBSD and DragonflyBSD.
#define OSTD_PLATFORM_BSD
/// Defined on Solaris, Illumos and derivatives.
#define OSTD_PLATFORM_SOLARIS
/// Defined when compiling with Clang.
#define OSTD_TOOLCHAIN_CLANG
/// Defined when compiling with GCC or a compatible compiler such as Clang.
#define OSTD_TOOLCHAIN_GNU
/// Defined when compiling with Microsoft Visual Studio.
#define OSTD_TOOLCHAIN_MSVC
/// The value of #OSTD_BYTE_ORDER on little-endian systems.
#define OSTD_ENDIAN_LIL
/// The value of #OSTD_BYTE_ORDER on big-endian systems.
#define OSTD_ENDIAN_BIG
/// The system's byte order, either #OSTD_ENDIAN_LIL or #OSTD_ENDIAN_BIG.
#define OSTD_BYTE_ORDER
/** @brief Use this to annotate externally visible API.
*
* On Windows, this will expand differently when building a library and when
2018-01-17 22:56:39 +01:00
* using the resulting API. You can define `OSTD_BUILD` to declare that you
* are currently building the library, this is only useful for build systems
* that build libostd itself, never for library usage. You can also define
* `OSTD_DLL` to declare the libostd library is a dynamic one, when both
* using and building the library. If `OSTD_DLL` is defined, then this
* will be either `__declspec(dllexport)` or `__declspec(dllimport)`,
* depending on whether `OSTD_BUILD` is defined, otherwise it will be
* empty regardless of `OSTD_BUILD`.
2017-03-27 18:51:50 +02:00
* On POSIX compliant systems, there are two possibilities. If your compiler
* supports it, it can expand to `__attribute__((visibility("default")))`.
2018-01-17 22:56:39 +01:00
* Otherwise, it will be empty. The `OSTD_BUILD` macro is not used then,
* neither is `OSTD_DLL`.
2017-03-27 18:51:50 +02:00
*
* ~~~{.cc}
* OSTD_EXPORT void foo();
*
* class OSTD_EXPORT bar {
* OSTD_LOCAL void private_method(); // internal to the DSO
*
* public:
* void public_method();
* };
*
* OSTD_EXPORT extern baz external_variable;
* ~~~
*
* @see OSTD_LOCAL
*/
#define OSTD_EXPORT
/** @brief Use this to annotate APIs internal to the library.
*
* On Windows, this will always expand to nothing. On POSIX compliant systems,
* it will expand to `__attribute__((visibility("hidden")))` on compliant
* compilers, otherwise also nothing. This macro is useful typically to hide
* private methods in classes that have been exported as whole. Otherwise,
* APIs that are not annontated can be considered local by default (though
* the actual visibility depends on your compiler flags).
*/
#define OSTD_LOCAL
/** @brief A convenience macro for the `cdecl` calling convention on Win32.
*
* This will expand to nothing on all platforms except 32-bit Windows, where
* default calling convention can be overridden. You can use this to enforce
* the `cdecl` calling convention for external function declarations where
* necessary without having to write Windows specific code.
*/
#define OSTD_CDECL
#else /* OSTD_GENERATING_DOC */
#if defined(WIN32) || defined(_WIN32) || (defined(__WIN32) && !defined(__CYGWIN__))
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_WIN32 1
# if defined(WIN64) || defined(_WIN64)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_WIN64 1
# endif
#else
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_POSIX 1
# if defined(__linux__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_LINUX 1
# endif
# if defined(__APPLE__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_OSX 1
# endif
# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_FREEBSD 1
# define OSTD_PLATFORM_BSD 1
# endif
# if defined(__NetBSD__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_NETBSD 1
# define OSTD_PLATFORM_BSD 1
# endif
# if defined(__OpenBSD__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_OPENBSD 1
# define OSTD_PLATFORM_BSD 1
# endif
# if defined(__DragonFly__)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_DRAGONFLYBSD 1
# define OSTD_PLATFORM_BSD 1
# endif
# if defined(sun) || defined(__sun)
2015-07-13 21:08:55 +02:00
# define OSTD_PLATFORM_SOLARIS 1
# endif
#endif
#if defined(__clang__)
2015-07-13 21:08:55 +02:00
# define OSTD_TOOLCHAIN_CLANG 1
#endif
#if defined(__GNUC__)
2015-07-13 21:08:55 +02:00
# define OSTD_TOOLCHAIN_GNU 1
#endif
#if defined(_MSC_VER)
2015-07-13 21:08:55 +02:00
# define OSTD_TOOLCHAIN_MSVC 1
#endif
2015-07-13 21:08:55 +02:00
#define OSTD_ENDIAN_LIL 1234
#define OSTD_ENDIAN_BIG 4321
2015-07-13 21:08:55 +02:00
#ifdef OSTD_PLATFORM_LINUX
# include <endian.h>
2015-07-13 21:08:55 +02:00
# define OSTD_BYTE_ORDER __BYTE_ORDER
#else
# if defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || \
defined(__AARCH64EB__) || defined(__MIPSEB__) || defined(__MIPSEB) || \
defined(_MIPSEB) || defined(__ppc__) || defined(__POWERPC__) || \
defined(_M_PPC) || defined(__sparc__)
2015-07-13 21:08:55 +02:00
# define OSTD_BYTE_ORDER OSTD_ENDIAN_BIG
# else
2015-07-13 21:08:55 +02:00
# define OSTD_BYTE_ORDER OSTD_ENDIAN_LIL
# endif
#endif
#ifdef OSTD_PLATFORM_WIN32
2018-01-17 22:56:39 +01:00
# ifdef OSTD_DLL
# ifdef OSTD_BUILD
# define OSTD_EXPORT __declspec(dllexport)
# else
2018-01-17 22:56:39 +01:00
# define OSTD_EXPORT __declspec(dllimport)
# endif
2017-03-10 03:05:05 +01:00
# else
2018-01-17 22:56:39 +01:00
# define OSTD_EXPORT
# endif
# define OSTD_LOCAL
#else
2018-01-17 22:56:39 +01:00
# if defined(OSTD_BUILD) || defined(OSTD_DLL)
/* -Wunused-macros */
# endif
# if __GNUC__ >= 4
# define OSTD_EXPORT __attribute__((visibility("default")))
# define OSTD_LOCAL __attribute__((visibility("hidden")))
# else
# define OSTD_EXPORT
# define OSTD_LOCAL
2015-09-30 20:18:09 +02:00
# endif
#endif
#if defined(OSTD_PLATFORM_WIN32) && !defined(OSTD_PLATFORM_WIN64)
# define OSTD_CDECL __cdecl
#else
# define OSTD_CDECL
#endif
2017-03-27 18:51:50 +02:00
#endif /* OSTD_GENERATING_DOC */
2015-07-13 21:07:14 +02:00
namespace ostd {
2015-06-27 03:05:25 +02:00
2017-03-27 18:51:50 +02:00
/** @addtogroup Platform
* @{
*/
2017-04-12 19:12:09 +02:00
#if defined(OSTD_TOOLCHAIN_GNU) && !defined(OSTD_GENERATING_DOC)
2015-06-27 03:05:25 +02:00
/* using gcc/clang builtins */
2017-01-30 19:19:09 +01:00
inline std::uint16_t endian_swap16(std::uint16_t x) noexcept {
2015-06-27 03:05:25 +02:00
return __builtin_bswap16(x);
}
2017-01-30 19:19:09 +01:00
inline std::uint32_t endian_swap32(std::uint32_t x) noexcept {
2015-06-27 03:05:25 +02:00
return __builtin_bswap32(x);
}
2017-01-30 19:19:09 +01:00
inline std::uint64_t endian_swap64(std::uint64_t x) noexcept {
2015-06-27 03:05:25 +02:00
return __builtin_bswap64(x);
}
2017-04-12 19:12:09 +02:00
#elif defined(OSTD_TOOLCHAIN_MSVC) && !defined(OSTD_GENERATING_DOC)
2015-06-27 03:05:25 +02:00
/* using msvc builtins */
2017-01-30 19:19:09 +01:00
inline std::uint16_t endian_swap16(std::uint16_t x) noexcept {
2015-06-27 03:05:25 +02:00
return _byteswap_ushort(x);
}
2017-01-30 19:19:09 +01:00
inline std::uint32_t endian_swap32(std::uint32_t x) noexcept {
2015-06-27 03:05:25 +02:00
/* win64 is llp64 */
return _byteswap_ulong(x);
}
2017-01-30 19:19:09 +01:00
inline std::uint64_t endian_swap64(std::uint64_t x) noexcept {
2015-06-27 03:05:25 +02:00
return _byteswap_uint64(x);
}
#else
/* fallback */
2017-04-12 19:12:09 +02:00
/** @brief 16-bit byte swap. */
2017-01-30 19:19:09 +01:00
inline std::uint16_t endian_swap16(std::uint16_t x) noexcept {
2015-06-27 03:05:25 +02:00
return (x << 8) | (x >> 8);
}
2017-04-12 19:12:09 +02:00
/** @brief 32-bit byte swap. */
2017-01-30 19:19:09 +01:00
inline std::uint32_t endian_swap32(std::uint32_t x) noexcept {
2015-06-27 03:05:25 +02:00
return (x << 24) | (x >> 24) | ((x >> 8) & 0xFF00) | ((x << 8) & 0xFF0000);
}
2017-04-12 19:12:09 +02:00
/** @brief 64-bit byte swap. */
2017-01-30 19:19:09 +01:00
inline std::uint64_t endian_swap64(std::uint64_t x) noexcept {
return endian_swap32(
2017-01-30 19:19:09 +01:00
std::uint32_t(x >> 32)) |
(std::uint64_t(endian_swap32(std::uint32_t(x))) << 32
);
2015-06-27 03:05:25 +02:00
}
#endif
2017-02-08 01:06:50 +01:00
/* endian swap */
2017-04-09 16:44:45 +02:00
template<
typename T, std::size_t N = sizeof(T),
bool IsNum = std::is_arithmetic_v<T>
>
2017-02-16 20:39:05 +01:00
struct endian_swap;
2017-02-08 01:06:50 +01:00
template<typename T>
2017-02-16 20:39:05 +01:00
struct endian_swap<T, 2, true> {
2017-02-08 01:06:50 +01:00
T operator()(T v) const {
union { T iv; std::uint16_t sv; } u;
u.iv = v;
u.sv = endian_swap16(u.sv);
return u.iv;
}
};
template<typename T>
2017-02-16 20:39:05 +01:00
struct endian_swap<T, 4, true> {
2017-02-08 01:06:50 +01:00
T operator()(T v) const {
union { T iv; std::uint32_t sv; } u;
u.iv = v;
u.sv = endian_swap32(u.sv);
return u.iv;
}
};
template<typename T>
2017-02-16 20:39:05 +01:00
struct endian_swap<T, 8, true> {
2017-02-08 01:06:50 +01:00
T operator()(T v) const {
union { T iv; std::uint64_t sv; } u;
u.iv = v;
u.sv = endian_swap64(u.sv);
return u.iv;
}
};
2017-03-27 18:51:50 +02:00
template<typename T, bool IsNum = std::is_arithmetic_v<T>>
struct from_lil_endian;
2017-02-08 01:06:50 +01:00
template<typename T>
2017-03-27 18:51:50 +02:00
struct from_lil_endian<T, true> {
T operator()(T v) const {
if constexpr(OSTD_BYTE_ORDER == OSTD_ENDIAN_LIL) {
return v;
} else {
return endian_swap<T>{}(v);
}
}
};
template<typename T, bool IsNum = std::is_arithmetic_v<T>>
struct from_big_endian;
2017-02-08 01:06:50 +01:00
template<typename T>
2017-03-27 18:51:50 +02:00
struct from_big_endian<T, true> {
T operator()(T v) const {
if constexpr(OSTD_BYTE_ORDER != OSTD_ENDIAN_LIL) {
return v;
} else {
return endian_swap<T>{}(v);
}
}
};
/** @} */
2017-02-08 01:06:50 +01:00
2015-06-27 03:05:25 +02:00
}
2016-02-07 22:17:15 +01:00
#endif
2017-03-27 18:51:50 +02:00
/** @} */