GNU/Linux:
cmake -S . -B build && cmake --build build && ./size_constructible
Windows:
cmake -S . -B build_win -G Ninja && cmake --build build_win && .\size_constructible
template <typename T, typename = void> | |
struct is_size_constructible : std::false_type {}; | |
template <typename T> | |
struct is_size_constructible<T, std::void_t<typename T::size_type>> | |
: std::is_constructible<T, typename T::size_type> {}; | |
template <typename T> | |
inline constexpr bool is_size_constructible_v = is_size_constructible<T>::value; |
# ignore build artifacts | |
build | |
build_win | |
size_constructible | |
*.exe | |
*.ilk | |
*.pdb |
GNU/Linux:
cmake -S . -B build && cmake --build build && ./size_constructible
Windows:
cmake -S . -B build_win -G Ninja && cmake --build build_win && .\size_constructible
cmake_minimum_required(VERSION 3.16) | |
project( | |
size_constructible | |
VERSION 0.1.0 | |
DESCRIPTION | |
"SFINAE approach to checking if a Container is \"size-constructible\"." | |
HOMEPAGE_URL | |
https://gist.github.com/phetdam/5ba39c8f7a8966ffe391612f54581a41 | |
LANGUAGES CXX | |
) | |
set(CMAKE_CXX_STANDARD 17) | |
set(CMAKE_CXX_STANDARD_REQUIRED ON) | |
add_executable(size_constructible size_constructible.cc) | |
set_property( | |
TARGET size_constructible | |
PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR} | |
) |
/** | |
* @file size_constructible.cc | |
* @author Derek Huang | |
* @brief SFINAE approach to checking if a *Container* is "size-constructible" | |
* @copyright MIT License | |
*/ | |
#include <cstdlib> | |
#include <iostream> | |
#include <list> | |
#include <type_traits> | |
#include <typeinfo> | |
#include <vector> | |
// GCC, clang use Itanium ABI so typeid(T).name() returns a mangled name. on | |
// Windows, if compiling with MSVC, typeid(T).name() returns "class T". | |
#if defined(__GNUG__) || defined(__clang__) | |
#include <cxxabi.h> | |
/** | |
* Return the demangled GCC/Clang type name for a type and set an error status. | |
* | |
* Since the demangled name is allocated on heap, `dest` must be freed. | |
* | |
* @param type C++ type name | |
* @param status_addr `int*` address to set error status | |
* @returns `char*` giving the demangled type name for `type` | |
*/ | |
#define GNU_DEMANGLED_TYPE_NAME(type, status_addr) \ | |
abi::__cxa_demangle(typeid(type).name(), NULL, NULL, status_addr) | |
/** | |
* Exit the program with the given error status if it is nonzero. | |
* | |
* @param status Error status set by `abi::__cxa_demangle` call | |
*/ | |
#define GNU_DEMANGLE_STATUS_HANDLER(status) \ | |
do { \ | |
if (status) { \ | |
std::cerr << "abi::__cxa_demangle error status " << status << std::endl; \ | |
std::exit(status); \ | |
} \ | |
} \ | |
while (0) | |
#endif // defined(__GNUG__) || defined(__clang__) | |
namespace { | |
/** | |
* Template struct to check if a *Container* is "size-constructible." | |
* | |
* Here "size-constructible" is taken to mean that a *Container* type `T` has a | |
* constructor `T(size_type N)` that reserves space for `N` elements. | |
* | |
* When `T` has the `size_type` member, the below partial specialization will | |
* be deduced from `is_size_constructible<T>`, which then will use | |
* `std::is_constructible` to specialize into `std::true_type` if | |
* `T(size_type N)` is a constructor overload, else `std::false_type`. | |
* | |
* `is_size_constructible_v` is an `inline constexpr bool` helper for the | |
* `is_size_constructible<T>::value` truth value. | |
* | |
* @tparam T *Container* type | |
* @tparam _ `std::void_t` | |
*/ | |
template <typename T, typename = void> | |
struct is_size_constructible : std::false_type {}; | |
template <typename T> | |
struct is_size_constructible<T, std::void_t<typename T::size_type>> | |
: std::is_constructible<T, typename T::size_type> {}; | |
template <typename T> | |
inline constexpr bool is_size_constructible_v = is_size_constructible<T>::value; | |
} // namespace | |
int main() | |
{ | |
using T = std::vector<double>; | |
using U = std::list<float>; | |
#if defined(__GNUG__) || defined(__clang__) | |
int status; | |
char* t_name = GNU_DEMANGLED_TYPE_NAME(T, &status); | |
GNU_DEMANGLE_STATUS_HANDLER(status); | |
char* u_name = GNU_DEMANGLED_TYPE_NAME(T, &status); | |
GNU_DEMANGLE_STATUS_HANDLER(status); | |
#else | |
const char* t_name = typeid(T).name(); | |
const char* u_name = typeid(U).name(); | |
#endif // defined(__GNUG__) || defined(__clang__) | |
static_assert(is_size_constructible_v<T> && is_size_constructible_v<U>); | |
std::cout << t_name << " is size-constructible\n"; | |
std::cout << u_name << " is size-constructible" << std::endl; | |
// t_name, u_name are malloc'd and so we must free | |
#if defined(__GNUG__) || defined(__clang__) | |
std::free(t_name); | |
std::free(u_name); | |
#endif // defined(__GNUG__) || defined(__clang__) | |
return EXIT_SUCCESS; | |
} |