Last active
May 25, 2023 07:43
-
-
Save dakka/a092eeb6650e8555b0976341fcd3d51d to your computer and use it in GitHub Desktop.
make_pow_array - create compiler generated constexpr C++20 arbitrary base and size power tables as std::array. Examples include uint128_t, bases 2, 3, 5, 8, 10, with constexpr printer and demangler. Uses folding, variable templates, constraints.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------------------------- | |
// Copyright (C) 2022-23 Fix8 Market Technologies Pty Ltd | |
// by David L. Dight | |
// | |
// make_pow_array - create compiler generated constexpr C++20 arbitrary base and size power | |
// tables as std::array. Examples include uint128_t, bases 2, 3, 5, 8, 10, with constexpr | |
// printer and demangler. Uses folding, variable templates, constraints. | |
// | |
// Distributed under the Boost Software License, Version 1.0 August 17th, 2003 | |
// | |
// Permission is hereby granted, free of charge, to any person or organization | |
// obtaining a copy of the software and accompanying documentation covered by | |
// this license (the "Software") to use, reproduce, display, distribute, | |
// execute, and transmit the Software, and to prepare derivative works of the | |
// Software, and to permit third-parties to whom the Software is furnished to | |
// do so, all subject to the following: | |
// | |
// The copyright notices in the Software and this entire statement, including | |
// the above license grant, this restriction and the following disclaimer, | |
// must be included in all copies of the Software, in whole or in part, and | |
// all derivative works of the Software, unless such copies or derivative | |
// works are solely in the form of machine-executable object code generated by | |
// a source language processor. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT | |
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE | |
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, | |
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
// DEALINGS IN THE SOFTWARE. | |
//---------------------------------------------------------------------------------------- | |
#include <iostream> | |
#include <string_view> | |
#include <string> | |
#include <array> | |
#include <limits> | |
#include <concepts> | |
#include <memory> | |
#include <typeinfo> | |
#include <utility> | |
#if defined(__clang__) || defined(__GNUC__) | |
#include <cxxabi.h> | |
#endif | |
//----------------------------------------------------------------------------------------- | |
/*! pow variable template | |
\tparam T - type to define | |
\tparam base - base of power | |
\tparam val - exponent to raise */ | |
template<typename T, int base, int val> | |
requires (base >= 0 && val >= 0) | |
constexpr T pow { T(base) * pow<T, base, val - 1> }; | |
/*! pow variable template - 0 partial specialisation | |
\tparam T - type to define | |
\tparam base - base of power */ | |
template<typename T, int base> | |
constexpr T pow<T, base, 0> { T(1) }; | |
/*! Create a std::array of base with supplied sequence; called by make_pow_array | |
\tparam T - type to generate | |
\tparam base - base of power | |
\tparam Vals - sequence of T | |
\return std::array */ | |
template<typename T, int base, T... Vals> | |
constexpr auto create_pow_array(std::integer_sequence<T, Vals...>) noexcept | |
{ | |
return std::array<T, sizeof...(Vals)>{ (pow<T, base, Vals>)...}; | |
} | |
/*! Calculate maximum digits that can be expressed for a given integral type and radix (at compile time) | |
\tparam T - type | |
\tparam base - radix, default base 10 | |
\return int max digits */ | |
template<std::integral T, int base=10, T mval=std::numeric_limits<T>::max()> | |
requires (base > 0) | |
constexpr int max_digits() noexcept | |
{ | |
if constexpr (mval > 0) | |
return 1 + max_digits<T, base, mval / base>(); | |
else | |
return 0; | |
} | |
/*! Make a std::array of base^cnt | |
\tparam T - type to generate | |
\tparam base - base of power | |
\tparam cnt - count of elements to generate | |
\return std::array */ | |
template<std::integral T, int base, int cnt> | |
requires (cnt <= max_digits<T, base>()) | |
constexpr auto make_pow_array() noexcept | |
{ | |
return create_pow_array<T, base>(std::make_integer_sequence<T, cnt>{}); | |
} | |
/*! Make a std::array of base^cnt with max digits that can be expressed for a given integral type and radix (at compile time) | |
\tparam T - type to generate | |
\tparam base - base of power, default 10 | |
\return std::array */ | |
template<std::integral T, int base=10> | |
constexpr auto make_pow_array() noexcept | |
{ | |
return create_pow_array<T, base>(std::make_integer_sequence<T, max_digits<T, base>()>{}); | |
} | |
//----------------------------------------------------------------------------------------- | |
//----------------------------------------------------------------------------------------- | |
// example | |
//----------------------------------------------------------------------------------------- | |
#if !defined uint128_t | |
using uint128_t = __uint128_t; | |
#endif | |
/*! unsigned 128 bit integer ostream inserter | |
\param os - std::ostream to insert into | |
\param val - const uint128_t& value to insert | |
\return os */ | |
std::ostream& operator<<(std::ostream& os, const uint128_t& val) noexcept | |
{ | |
if (std::ostream::sentry sent(os); sent) | |
{ | |
std::array<char, 128> buffer; | |
auto back { buffer.end() }; | |
for (auto tmp { val }; tmp; tmp /= 10) | |
*--back = (tmp % 10) + '0'; | |
if (auto len { buffer.end() - back }; os.rdbuf()->sputn(back, len) != len) | |
os.setstate(std::ios_base::badbit); | |
} | |
return os; | |
} | |
//----------------------------------------------------------------------------------------- | |
/*! IA64 ABI demangler | |
\tparam T - type to demangle | |
\return std::string */ | |
template<typename T> | |
constexpr std::string demangle() noexcept | |
{ | |
#if defined(__clang__) || defined(__GNUC__) | |
int status; | |
std::unique_ptr<char, decltype(&free)> eptr(abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), &free); | |
using namespace std::string_literals; | |
switch (status) | |
{ | |
case 0: return eptr.get(); | |
case -1: return "memory allocation failiure"s; | |
case -2: return "invalid mangled name"s; | |
default: | |
case -3: return "invalid argument"s; | |
} | |
#endif | |
return typeid(T).name(); | |
} | |
//----------------------------------------------------------------------------------------- | |
int main() | |
{ | |
static constexpr auto ptab0 { make_pow_array<unsigned long long, 2>() }; // power of 2 | |
static constexpr auto ptab1 { make_pow_array<unsigned short, 2>() }; | |
static constexpr auto ptab2 { make_pow_array<long, 2>() }; | |
static constexpr auto ptab3 { make_pow_array<uint128_t, 2>() }; | |
static constexpr auto ptab4 { make_pow_array<unsigned long long>() }; // power of 10 | |
static constexpr auto ptab5 { make_pow_array<uint128_t>() }; | |
// alternative radix | |
static constexpr auto ptab6 { make_pow_array<unsigned, 3, 21>() }; // power of 3 | |
static constexpr auto ptab7 { make_pow_array<unsigned, 3>() }; | |
static constexpr auto ptab8 { make_pow_array<unsigned, 5>() }; // power of 5 | |
static constexpr auto ptab9 { make_pow_array<uint128_t, 8, 43>() }; // power of 8 | |
static constexpr auto ptab10 { make_pow_array<uint128_t, 8>() }; | |
static_assert(pow<unsigned long long, 10, 4> == ptab4[4] && ptab4[4] == 10000ULL); | |
constexpr auto print([]<typename... Arrs>(const Arrs& ...arrs) constexpr noexcept | |
{ | |
([]<typename T>(const T& arr) constexpr noexcept | |
{ | |
std::cout << "// " << demangle<T>() << ", element size = " << sizeof(typename T::value_type) << ", radix = " << arr[1] << '\n'; | |
for (const auto& pp : arr) | |
std::cout << pp << '\n'; | |
std::cout << '\n'; | |
}(arrs), ...); | |
}); | |
print(ptab0, ptab1, ptab2, ptab3, ptab4, ptab5, ptab6, ptab7, ptab8, ptab9, ptab10); | |
std::cout << "10^4 = " << ptab5[4] << '\n' | |
<< "5^7 = " << ptab8[7] << '\n' | |
<< "2^126 = " << ptab5[126] << '\n' | |
<< "8^41 = " << ptab10[41] << '\n' | |
<< "6^4 = " << pow<int, 6, 4> << '\n'; | |
constexpr auto print_limits([]<typename... Lims>(Lims ...lims) constexpr noexcept | |
{ | |
([]<typename T>(T max) constexpr noexcept | |
{ | |
std::cout << "type:" << demangle<T>() << " max:" << max | |
<< " base10 digits:" << max_digits<T>() << " base2 digits:" << max_digits<T, 2>() << std::endl; | |
}(lims), ...); | |
}); | |
print_limits(std::numeric_limits<int>::max(), std::numeric_limits<unsigned>::max(), std::numeric_limits<short>::max(), | |
std::numeric_limits<unsigned short>::max(), std::numeric_limits<uint128_t>::max()); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To build
Example output