Last active
February 15, 2016 22:17
-
-
Save iscgar/9ba8fc9348af4389b26b to your computer and use it in GitHub Desktop.
Abusing type lists for easier generic development
This file contains hidden or 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
#include <stdio.h> | |
#include <string.h> | |
#include <ctype.h> | |
namespace detail | |
{ | |
template<typename T> | |
bool detail_echo(const T *obj, const char *arg) | |
{ | |
if (!obj) | |
{ | |
return false; | |
} | |
obj->echo(arg); | |
return true; | |
} | |
} | |
struct nil_tl {}; | |
template<typename T, typename U = nil_tl> | |
struct typelist | |
{ | |
T head; | |
U tail; | |
}; | |
template<typename TL> class echoer; | |
template<typename H> | |
class echoer<typelist<H, nil_tl> > | |
{ | |
public: | |
inline echoer() : m_object(), m_echoer() {} | |
inline bool init(const char *name) | |
{ | |
bool can_set = m_object.equals(name); | |
if (can_set) | |
{ | |
this->m_echoer.set(detail::detail_echo<H>, &m_object); | |
} | |
return can_set; | |
} | |
inline bool echo(const char *val) { return this->m_echoer.call(val); } | |
inline void reset() { this->m_echoer.set(NULL, NULL); } | |
protected: | |
template<typename V> | |
inline void reset(bool (*func)(const V *, const char *), const V *echoer) | |
{ | |
this->m_echoer.set(func, echoer); | |
} | |
private: | |
struct prv_detail | |
{ | |
inline prv_detail() : m_func(NULL), m_obj(NULL) {} | |
template<typename V> | |
inline void set(bool (*func)(const V *, const char *), const V *echoer) | |
{ | |
this->m_func = reinterpret_cast<bool (*)(const void *, const char *)>(func); | |
this->m_obj = echoer; | |
} | |
inline bool call(const char *value) | |
{ | |
if (!this->m_func) | |
{ | |
return false; | |
} | |
// UB? | |
return (*this->m_func)(this->m_obj, value); | |
} | |
private: | |
bool (*m_func)(const void *, const char *); | |
const void *m_obj; | |
}; | |
const H m_object; | |
prv_detail m_echoer; | |
}; | |
template<typename H, typename T> | |
class echoer<typelist<H, T> > : public echoer<T> | |
{ | |
public: | |
inline echoer() : m_object() {} | |
using echoer<T>::echo; | |
using echoer<T>::reset; | |
bool init(const char *name); | |
private: | |
const H m_object; | |
}; | |
template<typename H, typename T> | |
bool echoer<typelist<H, T> >::init(const char *name) | |
{ | |
if (this->m_object.equals(name)) | |
{ | |
this->reset(detail::detail_echo<H>, &this->m_object); | |
return true; | |
} | |
return echoer<T>::init(name); | |
} | |
struct lower_echoer | |
{ | |
inline bool equals(const char *name) const { return name && stricmp(name, "lower") == 0; } | |
void echo(const char *value) const | |
{ | |
while (*value) | |
{ | |
fputc(tolower(*value), stdout); | |
++value; | |
} | |
} | |
}; | |
struct upper_echoer | |
{ | |
inline bool equals(const char *name) const { return name && stricmp(name, "upper") == 0; } | |
void echo(const char *value) const | |
{ | |
while (*value) | |
{ | |
fputc(toupper(*value), stdout); | |
++value; | |
} | |
} | |
}; | |
struct normal_echoer : lower_echoer, upper_echoer | |
{ | |
virtual bool equals(const char *name) const { return name && stricmp(name, "normal") == 0; } | |
virtual void echo(const char *value) const | |
{ | |
char data[] = " "; | |
bool begin = true; | |
while (*value) | |
{ | |
data[0] = *value; | |
if (begin) | |
{ | |
upper_echoer::echo(data); | |
} | |
else | |
{ | |
lower_echoer::echo(data); | |
} | |
begin = isspace(*value); | |
++value; | |
} | |
} | |
}; | |
struct rnormal_echoer : normal_echoer | |
{ | |
virtual bool equals(const char *name) const { return name && stricmp(name, "rnormal") == 0; } | |
virtual void echo(const char *value) const | |
{ | |
char data[] = " "; | |
bool end = false; | |
while (*value) | |
{ | |
data[0] = *value; | |
end = !isalpha(value[1]); | |
if (end) | |
{ | |
upper_echoer::echo(data); | |
} | |
else | |
{ | |
lower_echoer::echo(data); | |
} | |
++value; | |
} | |
} | |
}; | |
int main(int argc, char const *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
printf("Usage: echoer <name> [words ...]\n"); | |
} | |
else | |
{ | |
typedef typelist<lower_echoer, typelist<upper_echoer, typelist<normal_echoer, typelist<rnormal_echoer, nil_tl> > > > echoers; | |
echoer<echoers> ech_handler; | |
if (!ech_handler.init(argv[1])) | |
{ | |
fprintf(stderr, "ERROR: Could not find `%s\'\n", argv[1]); | |
} | |
else if (argc > 2) | |
{ | |
switch (argc) | |
{ | |
default: | |
for (int i = 2; i < argc - 1; ++i) | |
{ | |
ech_handler.echo(argv[i]); | |
ech_handler.echo(" "); | |
} | |
case 3: | |
ech_handler.echo(argv[argc - 1]); | |
ech_handler.echo("\n"); | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment