Skip to content

Instantly share code, notes, and snippets.

@phetdam
Last active December 3, 2022 03:56
Show Gist options
  • Save phetdam/5ba39c8f7a8966ffe391612f54581a41 to your computer and use it in GitHub Desktop.
Save phetdam/5ba39c8f7a8966ffe391612f54581a41 to your computer and use it in GitHub Desktop.
SFINAE approach to checking if a Container is "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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment