Skip to content

Commit

Permalink
pw_minimal_cpp_stdlib: Expand to cover pw_tokenizer
Browse files Browse the repository at this point in the history
- Add logical type traits (conjunction, disjunction, negation),
  make_signed/make_unsigned, and index_sequence.
- Set up a toolchain for building with pw_minimal_cpp_stdlib instead
  of the toolchain's C++ Standard Library.
- Warn against using pw_minimal_cpp_stdlib in the docs.

Bug: b/246947634
Change-Id: I2f4317c508fa61a852163034f1dfb15158d0a89a
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/112370
Reviewed-by: Armando Montanez <amontanez@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
  • Loading branch information
255 authored and CQ Bot Account committed Nov 4, 2022
1 parent 1cad75b commit 47167b9
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 15 deletions.
10 changes: 10 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,16 @@ group("cpp20_compatibility") {
]
}

group("build_with_pw_minimal_cpp_stdlib") {
_toolchain = "$_internal_toolchains:pw_strict_host_clang_size_optimized_minimal_cpp_stdlib"

# This list of supported modules is incomplete.
deps = [
"$dir_pw_status($_toolchain)",
"$dir_pw_tokenizer($_toolchain)",
]
}

# The default toolchain is not used for compiling C/C++ code.
if (current_toolchain != default_toolchain) {
group("apps") {
Expand Down
12 changes: 10 additions & 2 deletions pw_minimal_cpp_stdlib/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,24 @@ import("$dir_pw_build/target_types.gni")
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_unit_test/test.gni")

config("include_dirs") {
config("public_include_path") {
include_dirs = [ "public" ]
visibility = [ ":*" ]
}

config("no_cpp_includes") {
cflags = [ "-nostdinc++" ]
}

config("use_minimal_cpp_stdlib") {
configs = [
":public_include_path",
":no_cpp_includes",
]
}

pw_source_set("pw_minimal_cpp_stdlib") {
public_configs = [ ":include_dirs" ]
public_configs = [ ":public_include_path" ]
configs = [ ":no_cpp_includes" ]
public = [
"public/algorithm",
Expand Down
30 changes: 24 additions & 6 deletions pw_minimal_cpp_stdlib/docs.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
.. _module-pw_minimal_cpp_stdlib:

---------------------
=====================
pw_minimal_cpp_stdlib
---------------------
=====================
The ``pw_minimal_cpp_stdlib`` module provides an extremely limited
implementation of the C++ Standard Library. This module falls far, far short of
providing a complete C++ Standard Library and should only be used in dire
situations where you happen to be compiling with C++17 but don't have a C++
Standard Library available to you.
providing a complete C++ Standard Library and should only be used for testing
and development when compiling with C++17 or newer without a C++ Standard
Library. Production code should use a real C++ Standard Library implementation,
such as `libc++ <https://libcxx.llvm.org/>`_ or
`libstdc++ <https://gcc.gnu.org/onlinedocs/libstdc++/>`_.

.. warning::

``pw_minimal_cpp_stdlib`` was created for a very specific purpose. It is NOT a
general purpose C++ Standard Library implementation and should not be used as
one. Many features are missing, some features non-functioning stubs, and some
features may not match the C++ standard.

-----------
Code layout
-----------
The C++ Standard Library headers (e.g. ``<cstdint>`` and ``<type_traits>``) are
defined in ``public/``. These files are symlinks to their implementations in
``public/internal/``.
Expand All @@ -22,8 +34,14 @@ defined in ``public/``. These files are symlinks to their implementations in
for f in $(ls internal/); do ln -s internal/$f ${f%.h}; done
The top-level ``build_with_minimal_cpp_stdlib`` GN group builds a few supported
modules with ``pw_minimal_cpp_stdlib`` swapped in for the C++ library at the
toolchain level. Notably, ``pw_minimal_cpp_stdlib`` does not support
``pw_unit_test``, so this group does not run any tests.

------------
Requirements
============
------------
- C++17
- gcc or clang
- The C Standard Library
35 changes: 35 additions & 0 deletions pw_minimal_cpp_stdlib/isolated_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,25 @@ TEST(TypeTraits, Basic) {
static_assert(!std::is_same_v<char, unsigned char>);
}

TEST(TypeTraits, LogicalTraits) {
static_assert(std::conjunction_v<>);
static_assert(!std::conjunction_v<std::false_type>);
static_assert(std::conjunction_v<std::true_type>);
static_assert(!std::conjunction_v<std::false_type, std::true_type>);
static_assert(std::conjunction_v<std::true_type, std::true_type>);
static_assert(!std::conjunction_v<std::false_type, std::false_type>);

static_assert(!std::disjunction_v<>);
static_assert(!std::disjunction_v<std::false_type>);
static_assert(std::disjunction_v<std::true_type>);
static_assert(std::disjunction_v<std::false_type, std::true_type>);
static_assert(std::disjunction_v<std::true_type, std::true_type>);
static_assert(!std::disjunction_v<std::false_type, std::false_type>);

static_assert(std::negation_v<std::false_type>);
static_assert(!std::negation_v<std::true_type>);
}

struct MoveTester {
MoveTester(int value) : magic_value(value), moved(false) {}

Expand Down Expand Up @@ -325,6 +344,22 @@ TEST(Utility, Move) {
EXPECT_TRUE(moved.moved);
}

TEST(Utility, MakeIntegerSequence) {
static_assert(std::is_same_v<std::make_integer_sequence<int, 0>,
std::integer_sequence<int>>);
static_assert(std::is_same_v<std::make_integer_sequence<int, 1>,
std::integer_sequence<int, 0>>);
static_assert(std::is_same_v<std::make_integer_sequence<int, 3>,
std::integer_sequence<int, 0, 1, 2>>);

static_assert(std::is_same_v<std::make_index_sequence<0>,
std::integer_sequence<size_t>>);
static_assert(std::is_same_v<std::make_index_sequence<1>,
std::integer_sequence<size_t, 0>>);
static_assert(std::is_same_v<std::make_index_sequence<3>,
std::integer_sequence<size_t, 0, 1, 2>>);
}

} // namespace

namespace pw::minimal_cpp_stdlib {
Expand Down
89 changes: 82 additions & 7 deletions pw_minimal_cpp_stdlib/public/internal/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ _PW_POLYFILL_BEGIN_NAMESPACE_STD

#define __cpp_lib_transformation_trait_aliases 201304L
#define __cpp_lib_type_trait_variable_templates 201510L
#define __cpp_lib_logical_traits 201510L

template <decltype(sizeof(0)) kLength,
decltype(sizeof(0)) kAlignment> // no default
Expand Down Expand Up @@ -209,12 +208,6 @@ struct is_void : is_same<void, typename remove_cv<T>::type> {};
template <typename T>
inline constexpr bool is_void_v = is_void<T>::value;

template <typename T>
struct negation : bool_constant<!bool(T::value)> {};

template <typename T>
inline constexpr bool negation_v = negation<T>::value;

template <bool kBool, typename TrueType, typename FalseType>
struct conditional {
using type = TrueType;
Expand All @@ -228,6 +221,46 @@ struct conditional<false, TrueType, FalseType> {
template <bool kBool, typename TrueType, typename FalseType>
using conditional_t = typename conditional<kBool, TrueType, FalseType>::type;

#define __cpp_lib_logical_traits 201510L

template <typename...>
struct conjunction;

template <>
struct conjunction<> : true_type {};

template <typename T>
struct conjunction<T> : T {};

template <typename First, typename... Others>
struct conjunction<First, Others...>
: conditional_t<bool(First::value), conjunction<Others...>, First> {};

template <typename... Types>
inline constexpr bool conjunction_v = conjunction<Types...>::value;

template <typename...>
struct disjunction;

template <>
struct disjunction<> : false_type {};

template <typename T>
struct disjunction<T> : T {};

template <typename First, typename... Others>
struct disjunction<First, Others...>
: conditional_t<bool(First::value), First, disjunction<Others...>> {};

template <typename... Types>
inline constexpr bool disjunction_v = disjunction<Types...>::value;

template <typename T>
struct negation : bool_constant<!bool(T::value)> {};

template <typename T>
inline constexpr bool negation_v = negation<T>::value;

template <bool kEnable, typename T = void>
struct enable_if {
using type = T;
Expand Down Expand Up @@ -398,6 +431,48 @@ using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;
template <typename T>
add_rvalue_reference_t<T> declval() noexcept;

template <typename T>
struct make_signed;

template <typename T>
struct make_unsigned;

#define _PW_MAKE_SIGNED_SPECIALIZATION(base, signed_type, unsigned_type) \
template <> \
struct make_signed<base> { \
using type = signed_type; \
}; \
template <> \
struct make_unsigned<base> { \
using type = unsigned_type; \
}

_PW_MAKE_SIGNED_SPECIALIZATION(char, signed char, unsigned char);
_PW_MAKE_SIGNED_SPECIALIZATION(signed char, signed char, unsigned char);
_PW_MAKE_SIGNED_SPECIALIZATION(unsigned char, signed char, unsigned char);

_PW_MAKE_SIGNED_SPECIALIZATION(short, signed short, unsigned short);
_PW_MAKE_SIGNED_SPECIALIZATION(unsigned short, signed short, unsigned short);

_PW_MAKE_SIGNED_SPECIALIZATION(int, signed int, unsigned int);
_PW_MAKE_SIGNED_SPECIALIZATION(unsigned int, signed int, unsigned int);

_PW_MAKE_SIGNED_SPECIALIZATION(long, signed long, unsigned long);
_PW_MAKE_SIGNED_SPECIALIZATION(unsigned long, signed long, unsigned long);

_PW_MAKE_SIGNED_SPECIALIZATION(long long, signed short, unsigned short);
_PW_MAKE_SIGNED_SPECIALIZATION(unsigned long long,
signed short,
unsigned short);

// Skip specializations for char8_t, etc.

template <typename T>
using make_signed_t = typename make_signed<T>::type;

template <typename T>
using make_unsigned_t = typename make_unsigned<T>::type;

namespace impl {

template <typename>
Expand Down
22 changes: 22 additions & 0 deletions pw_minimal_cpp_stdlib/public/internal/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,26 @@ struct tuple_element;
template <typename>
struct tuple_size;

template <typename T, T... kSequence>
class integer_sequence;

template <size_t... kSequence>
using index_sequence = integer_sequence<decltype(sizeof(int)), kSequence...>;

template <typename T, T kEnd>
#if __has_builtin(__make_integer_seq)
using make_integer_sequence = __make_integer_seq<integer_sequence, T, kEnd>;
#elif __has_builtin(__integer_pack)
using make_integer_sequence = integer_sequence<T, __integer_pack(kEnd)...>;
#endif // make_integer_sequence

template <size_t kEnd>
using make_index_sequence = make_integer_sequence<size_t, kEnd>;

struct in_place_t {
explicit constexpr in_place_t() = default;
};

inline constexpr in_place_t in_place{};

_PW_POLYFILL_END_NAMESPACE_STD
13 changes: 13 additions & 0 deletions targets/host/target_toolchains.gni
Original file line number Diff line number Diff line change
Expand Up @@ -435,4 +435,17 @@ pw_internal_host_toolchains = [
pw_toolchain_CXX_STANDARD = pw_toolchain_STANDARD.CXX20
}
},
{
name = "pw_strict_host_clang_size_optimized_minimal_cpp_stdlib"
_toolchain_base = pw_toolchain_host_clang.size_optimized
forward_variables_from(_toolchain_base, "*", _excluded_members)
defaults = {
forward_variables_from(_toolchain_base.defaults, "*")
forward_variables_from(_host_common, "*")
forward_variables_from(_pigweed_internal, "*")
forward_variables_from(_os_specific_config, "*")
default_configs += _internal_clang_default_configs
default_configs += [ "$dir_pw_minimal_cpp_stdlib:use_minimal_cpp_stdlib" ]
}
},
]

0 comments on commit 47167b9

Please sign in to comment.