Last active
March 30, 2023 06:58
-
-
Save kaidokert/c26c0c01c6216acf02f003bdc13542bf to your computer and use it in GitHub Desktop.
C++11 constexpr strcmp, from gcc 4.7.2+ and clang 3.5+
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
// https://godbolt.org/z/5G3Ah3 | |
// API: constexpr int const_strcmp( "foo", "bar" ); | |
// Much more readable version here: https://gist.github.com/kaidokert/dfc08be8e75a3fc650d3daf8e89c3fe9 | |
// but that doesn't work with GCC before version 7 | |
#include <cstddef> | |
#include <utility> | |
namespace detail { | |
using c_str = const char *; | |
template<size_t IDX, typename T> struct recursive_array {}; | |
template<size_t IDX,size_t N> // General case, Counter=0...N-1 | |
struct recursive_array<IDX, const char (&)[N]> { | |
constexpr static size_t cmp(const char(&a)[N], c_str b) { | |
return a[IDX] == '\0' ? | |
( b[IDX] == '\0' ? 0 : -1 ): | |
( | |
( a[IDX] != b[IDX] ) ? | |
( a[IDX] > b[IDX] ? 1 : -1) : | |
recursive_array<IDX + 1, const char(&)[N]>::cmp(a,b) | |
); | |
} | |
}; | |
template<size_t N> // Termination case, COUNTER = N | |
struct recursive_array<N, const char (&)[N]> { | |
constexpr static int cmp(const char(&)[N], c_str) { | |
return 0; | |
} | |
}; | |
template<size_t IDX, typename T, typename W> struct dual_recursive_array {}; | |
template<size_t IDX,size_t N1,size_t N2> // General case, Counter=0...N-1 | |
struct dual_recursive_array<IDX, const char (&)[N1],const char (&)[N2]> { | |
constexpr static size_t cmp(const char(&a)[N1], const char (&b)[N2]) { | |
return ((a[IDX] == '\0') || (IDX == N1)) ? | |
( b[IDX] == '\0' ? 0 : -1 ): | |
( | |
( a[IDX] != b[IDX] ) ? | |
( a[IDX] > b[IDX] ? 1 : -1) : | |
dual_recursive_array<IDX + 1, const char(&)[N1], const char(&)[N2]>::cmp(a,b) | |
); | |
} | |
}; | |
template<size_t N1,size_t N2> // Termination case, COUNTER = N1 | |
struct dual_recursive_array<N1, const char (&)[N1],const char (&)[N2]> { | |
constexpr static size_t cmp(const char(&)[N1], const char (&)[N2]) { | |
return N1 >= N2 ? 0 : -1; | |
} | |
}; | |
template<typename T, typename W> struct cmp_helper { | |
static constexpr int cmp( c_str a, c_str b) { | |
return *a == '\0' ? | |
( *b == '\0' ? 0 : -1) : | |
( | |
(*a != *b) ? | |
( *a > *b ? 1 : -1) : | |
cmp( a+1 , b+1) | |
); | |
} | |
}; | |
template<size_t N1> | |
struct cmp_helper<const char (&)[N1], std::nullptr_t > { | |
static constexpr int cmp( const char (&)[N1], std::nullptr_t) { | |
return 1; | |
} | |
}; | |
template<size_t N1, typename W> | |
struct cmp_helper<const char (&)[N1], W> { | |
static constexpr int cmp( const char (&x)[N1], c_str b) { | |
return recursive_array<0, const char (&)[N1]>::cmp(x, b); | |
} | |
}; | |
template<size_t N1, size_t N2> | |
struct cmp_helper<const char (&)[N1], const char (&)[N2]> { | |
static constexpr int cmp( const char (&x)[N1], const char (&y)[N2]) { | |
return dual_recarray<0, const char (&)[N1], const char (&)[N2]>::cmp(x,y); | |
} | |
}; | |
template<> struct cmp_helper<std::nullptr_t, std::nullptr_t> { | |
static constexpr int cmp( std::nullptr_t , std::nullptr_t) { return 0; } | |
}; | |
template<typename W> struct cmp_helper<std::nullptr_t, W> { | |
static constexpr int cmp( std::nullptr_t, c_str) { return -1; } | |
}; | |
struct NormalCompare{}; | |
struct SwapCompare{}; | |
template<typename L, typename R> | |
struct SelectCompare { // general version | |
using Type = NormalCompare; | |
}; | |
template<typename L> | |
struct SelectCompare<L,std::nullptr_t> { | |
using Type = SwapCompare; | |
}; | |
template<typename L, size_t N> | |
struct SelectCompare<L, const char(&)[N]> { | |
using Type = SwapCompare; | |
}; | |
template<size_t N1, size_t N2> | |
struct SelectCompare<const char(&)[N1], const char(&)[N2]> { | |
using Type = typename std::conditional<(N1 > N2),SwapCompare,NormalCompare>::type; | |
}; | |
template<typename L, typename R> | |
using SelectCompareT = typename SelectCompare<L, R>::Type; | |
template<typename LHS, typename RHS> | |
constexpr int cmp_(LHS&& param_a, RHS&& param_b, NormalCompare) { | |
return detail::cmp_helper< LHS , RHS >::cmp( param_a, param_b ); | |
} | |
template<typename LHS, typename RHS> | |
constexpr int cmp_(LHS&& param_a, RHS&& param_b, SwapCompare) { | |
return -1 * detail::cmp_helper< RHS , LHS >::cmp( param_b, param_a ); | |
} | |
}; | |
template<typename LHS, typename RHS> | |
constexpr int const_strcmp(LHS&& param_a, RHS&& param_b) { | |
return detail::cmp_( std::forward<LHS>(param_a) , std::forward<RHS>(param_b) | |
, detail::SelectCompareT<LHS, RHS>{} ); | |
} | |
// Tests | |
using c_str=const char *; | |
constexpr c_str a1 = "a"; | |
constexpr c_str b1 = "b"; | |
constexpr c_str a2 = "a"; | |
constexpr c_str b2 = "b"; | |
constexpr c_str foo = "foo"; | |
constexpr char * c1 = "a"; | |
constexpr const char real_long[] = | |
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sit amet erat accumsan, " | |
"pellentesque libero id, pulvinar diam. Cras ullamcorper velit vitae tortor ultricies, at semper mi consequat. " | |
"Vestibulum pretium aliquam sapien, sit amet"; | |
constexpr const char same_but_same[] = | |
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sit amet erat accumsan, " | |
"pellentesque libero id, pulvinar diam. Cras ullamcorper velit vitae tortor ultricies, at semper mi consequat. " | |
"Vestibulum pretium aliquam sapien, sit amet"; | |
constexpr const char same_but_different[] = | |
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sit amet erat accumsan, " | |
"pellentesque libero id, pulvinar diam. Aras ullamcorper velit vitae tortor ultricies, at semper mi consequat. " | |
"Vestibulum pretium aliquam sapien, sit amet"; | |
struct Cmp { | |
int cmp; | |
constexpr Cmp(c_str p0_,c_str p1_) : cmp(const_strcmp(p0_, p1_)) {} | |
}; | |
struct CmpA1R { | |
int cmp; | |
constexpr CmpA1R(c_str p_) : cmp(const_strcmp(p_, a1)) {} | |
}; | |
struct CmpA1L { | |
int cmp; | |
constexpr CmpA1L(c_str p_) : cmp(const_strcmp(a1,p_)) {} | |
}; | |
struct CmpC1R { | |
int cmp; | |
constexpr CmpC1R(c_str p_) : cmp(const_strcmp(p_, c1)) {} | |
}; | |
struct CmpC1L { | |
int cmp; | |
constexpr CmpC1L(c_str p_) : cmp(const_strcmp(c1, p_)) {} | |
}; | |
int main() { | |
static_assert(0 == const_strcmp(nullptr, nullptr), ""); | |
static_assert(-1 == const_strcmp(nullptr, a1), ""); | |
static_assert(1 == const_strcmp(a1, nullptr), ""); | |
static_assert(-1 == const_strcmp(a1, b1), ""); | |
static_assert(0 == const_strcmp(a1, a2), ""); | |
static_assert(1 == const_strcmp(b1, a1), ""); | |
static_assert(0 == const_strcmp(b1, b2), ""); | |
static_assert(0 == const_strcmp(c1, c1), ""); | |
static_assert(0 == const_strcmp(c1, a1), ""); | |
static_assert(0 == const_strcmp(a1, c1), ""); | |
static_assert(-1 == const_strcmp(c1, b1), ""); | |
static_assert(1 == const_strcmp(b1, c1), ""); | |
static_assert(-1 == Cmp(a1, b1).cmp, ""); | |
static_assert(1 == Cmp(b1, a1).cmp, ""); | |
static_assert(0 == Cmp(a1, a2).cmp, ""); | |
static_assert(0 == Cmp(b1, b2).cmp, ""); | |
static_assert(0 == Cmp(c1, c1).cmp, ""); | |
static_assert(0 == Cmp(c1, a1).cmp, ""); | |
static_assert(0 == Cmp(a1, c1).cmp, ""); | |
static_assert(-1 == Cmp(c1, b1).cmp, ""); | |
static_assert(1 == Cmp(b1, c1).cmp, ""); | |
static_assert(0 == CmpA1R(a1).cmp, ""); | |
static_assert(-1 == CmpA1L(b1).cmp, ""); | |
static_assert(0 == CmpC1R(a1).cmp, ""); | |
static_assert(-1 == CmpC1L(b1).cmp, ""); | |
static_assert(0 == const_strcmp("ab", "ab"), ""); | |
static_assert(1 == const_strcmp("bb", "ab"), ""); | |
static_assert(-1 == const_strcmp("ab", "bb"), ""); | |
constexpr const char ce_const_arr_empty[] = ""; | |
constexpr const char ce_const_arr[] = "foo"; | |
constexpr char ce_arr[] = "foo"; | |
constexpr const char unending[3] = {'f', 'o', 'o'}; | |
static_assert(-1 == const_strcmp(ce_const_arr_empty, "ab"), ""); | |
static_assert(1 == const_strcmp("ab", ce_const_arr_empty), ""); | |
static_assert(-1 == const_strcmp(nullptr, ce_const_arr), ""); | |
static_assert(1 == const_strcmp(ce_const_arr, nullptr), ""); | |
static_assert(0 == const_strcmp(ce_const_arr, foo), ""); | |
static_assert(0 == const_strcmp("foo", foo), ""); | |
static_assert(1 == const_strcmp("xyz", foo), ""); | |
static_assert(-1 == const_strcmp("eoo", foo), ""); | |
static_assert(0 == const_strcmp(foo, "foo"), ""); | |
static_assert(-1 == const_strcmp(foo, "xyz"), ""); | |
static_assert(1 == const_strcmp(foo, "eoo"), ""); | |
static_assert(0 == const_strcmp("foo", ce_arr), ""); | |
static_assert(1 == const_strcmp("xyz", ce_arr), ""); | |
static_assert(-1 == const_strcmp("eoo", ce_arr), ""); | |
static_assert(0 == const_strcmp(ce_arr, "foo"), ""); | |
static_assert(-1 == const_strcmp(ce_arr, "xyz"), ""); | |
static_assert(1 == const_strcmp(ce_arr, "eoo"), ""); | |
static_assert(0 == const_strcmp(ce_const_arr, ce_arr), ""); | |
static_assert(0 == const_strcmp(foo, unending), ""); | |
static_assert(0 == const_strcmp(unending, foo), ""); | |
constexpr char ab[3] = {'a', 'b', '\0'}; | |
constexpr const char ba[3] = {'b', 'a', '\0'}; | |
constexpr const char long_unterminated[] = {'a', 'a', 'a'}; | |
constexpr const char short_unterminated[] = {'a', 'a'}; | |
static_assert(-1 == const_strcmp(ab, ba), ""); | |
static_assert(1 == const_strcmp(ba, ab), ""); | |
static_assert(0 == const_strcmp(ab, "ab"), ""); | |
static_assert(0 == const_strcmp(ba, "ba"), ""); | |
static_assert(0 == const_strcmp("ab", ab), ""); | |
static_assert(0 == const_strcmp("ba", ba), ""); | |
static_assert(-1 == const_strcmp("aa", "ab"), ""); | |
static_assert(1 == const_strcmp("ab", "aa"), ""); | |
static_assert(1 == const_strcmp("abc", "aa"), ""); | |
static_assert(1 == const_strcmp("aaa", "aa"), ""); | |
static_assert(-1 == const_strcmp("aa", "abc"), ""); | |
static_assert(-1 == const_strcmp("aa", "aaaaa"), ""); | |
static_assert(-1 == const_strcmp("aa", "abc"), ""); | |
static_assert(-1 == const_strcmp(short_unterminated, "aaaaa"), ""); | |
static_assert( 1 == const_strcmp("aaaaa",short_unterminated), ""); | |
static_assert( 1 == const_strcmp(long_unterminated, "aa"), ""); | |
static_assert(-1 == const_strcmp("aa",long_unterminated), ""); | |
static_assert(-1 == const_strcmp(short_unterminated, long_unterminated), ""); | |
static_assert( 0 == const_strcmp(short_unterminated, short_unterminated), ""); | |
static_assert( 0 == const_strcmp(real_long, same_but_same), ""); | |
static_assert( 1 == const_strcmp(real_long, same_but_different), ""); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment