Skip to content

Instantly share code, notes, and snippets.

@qrealka
Forked from lichray/static_if.cc
Created May 10, 2017 07:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qrealka/ac92636753388a7dab897b860dbadd0b to your computer and use it in GitHub Desktop.
Save qrealka/ac92636753388a7dab897b860dbadd0b to your computer and use it in GitHub Desktop.
Implement static_if using C11 generic selection
#include <type_traits>
#include <tuple>
#include <iostream>
// Link: https://github.com/aeyakovenko/notes
//count arguments
//COUNT_ARGS :: ... -> Int
#define COUNT_ARGS(...) COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0)
#define COUNT_ARGS_(z,a,b,c,d,e,f,cnt,...) cnt
//call an object
//CALL :: (... -> a) -> (...) -> a
#define CALL(x,y) x y
//HEAD :: [a] -> a
#define HEAD(args) CALL(HEAD_,args)
#define HEAD_(a,...) a
//TAIL :: [a] -> [a]
#define TAIL(args) (CALL(TAIL_,args))
#define TAIL_(a,...) __VA_ARGS__
//UNPACK :: (...) -> ...
#define UNPACK(args) CALL(UNPACK_,args)
#define UNPACK_(...) __VA_ARGS__
#define ISEMPTY(ls) PASTE(ISEMPTY_,CALL_(COUNT_ARGS,PASTE(ISEMPTY_,CALL(COUNT_ARGS,ls))))
#define CALL_(f,a) f(a)
#define ISEMPTY_0 a,b
#define ISEMPTY_2 1
#define ISEMPTY_1 0
//JOIN :: (...) -> (...) -> (...)
#define JOIN(l1,l2) PASTE(JOIN_,PASTE(ISEMPTY(l1),ISEMPTY(l2)))(l1,l2)
#define JOIN_00(l1,l2) (UNPACK(l1),UNPACK(l2))
#define JOIN_10(l1,l2) l2
#define JOIN_01(l1,l2) l1
#define JOIN_11(l1,l2) ()
//curry a function to it arguments
#define CURRY(f,args) JOIN(f,args)
//evaluate a function object
#define EVAL(f) EVAL_ f
#define EVAL_(f,...) f(__VA_ARGS__)
//PASTE :: a -> b -> c
//this is really in the preprocessor domain, so hard to type
#define PASTE(aa,bb) PASTE_(aa,bb)
#define PASTE_(aa,bb) aa ## bb
//FOLDR :: (a -> b -> b) -> b -> [a] -> b
#define FOLDR(func,accum,lst) PASTE(FOLDR_,CALL(COUNT_ARGS,lst))(func,accum,lst)
#define FOLDR_0(func,accum,lst) accum
#define FOLDR_1(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst), accum)))
#define FOLDR_2(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst),FOLDR_1(func,accum,TAIL(lst)))))
#define FOLDR_3(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst),FOLDR_2(func,accum,TAIL(lst)))))
#define FOLDR_4(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst),FOLDR_3(func,accum,TAIL(lst)))))
#define FOLDR_5(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst),FOLDR_4(func,accum,TAIL(lst)))))
#define FOLDR_6(func,accum,lst) EVAL_FR(CURRY(func,(HEAD(lst),FOLDR_5(func,accum,TAIL(lst)))))
//the map needs a different eval then the foldr
//i dont fully grok this part yet
#define EVAL_FR(f) EVAL_FR_ f
#define EVAL_FR_(f,...) f(__VA_ARGS__)
//MAP :: (a -> b) -> [a] -> b
#define MAP(func,lst) FOLDR((MAP_,func),(),lst)
#define MAP_(func,aa,acc) JOIN((EVAL_MP(CURRY(func,(aa)))), acc)
#define EVAL_MP(f) EVAL_MP_ f
#define EVAL_MP_(f,...) f(__VA_ARGS__)
#define static_if(...) static_if__(__VA_ARGS__, \
static_if_4, static_if_3)(__VA_ARGS__)
#define static_if__(_1,_2,_3,_4,f,...) f
#define static_if_3(capture, cond, true_branch) \
_Generic(std::integral_constant<bool, bool(cond)>{}, \
std::true_type: []_as_glargs(capture) true_branch, \
default: []_as_glargs(capture) {})_as_ids(capture)
#define static_if_4(capture, cond, true_branch, false_branch) \
_Generic(std::integral_constant<bool, bool(cond)>{}, \
std::true_type: []_as_glargs(capture) true_branch, \
std::false_type: []_as_glargs(capture) false_branch)_as_ids(capture)
#define _as_ids(lst) MAP((_as_id), lst)
#define _as_id(a) _as_id_(a,)
#define _as_id_(a, b) a ## b
#define _as_glargs(lst) MAP((_as_glarg), lst)
#define _as_glarg(a) auto&& _as_id(a)
template <typename T>
void test_strlen(T const& s)
{
static_if((s), (std::is_same<T, std::string>{}),
{
std::cout << s.size() << std::endl;
},
{
std::cout << std::char_traits<char>::length(s) << std::endl;
});
}
template <int flag>
void test_empty()
{
static_if((), flag,
{
std::cout << "called" << std::endl;
});
}
void test_multi()
{
int a, b, c, d, e, f;
static_if((a, b, c, d, e, f), true,
{
std::tie(a, b, c, d, e, f) = std::make_tuple(1, 2, 3, 4, 5, 6);
});
std::cout << a << b << c << d << e << f << std::endl;
}
int main()
{
test_empty<1>();
test_empty<0>();
test_multi();
test_strlen("wor\0d");
test_strlen(std::string("wor\0d", 5));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment