Skip to content

Instantly share code, notes, and snippets.

@kaidokert
Last active March 30, 2023 06:58
Show Gist options
  • Save kaidokert/c26c0c01c6216acf02f003bdc13542bf to your computer and use it in GitHub Desktop.
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+
// 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