Skip to content

Instantly share code, notes, and snippets.

@Starl1ght
Created March 26, 2017 21:32
Show Gist options
  • Save Starl1ght/ddf43886d405df7aae072768455f3e16 to your computer and use it in GitHub Desktop.
Save Starl1ght/ddf43886d405df7aae072768455f3e16 to your computer and use it in GitHub Desktop.
Console
#include <vector>
#include <sstream>
#include <iostream>
#include <iterator>
#include <tuple>
template <typename T>
std::vector<std::string> Split(T&& input) {
std::stringstream ss{ std::forward<T>(input) };
return std::vector<std::string> {std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>{} };
}
// Throw format
inline void ThrowInternal(std::stringstream& ss) {
throw std::logic_error(ss.str());
}
template<typename T, typename...ARGS>
void ThrowInternal(std::stringstream& ss, T&& elem, ARGS&&...rest) {
ss << std::forward<T>(elem);
ThrowInternal(ss, std::forward<ARGS>(rest)...);
}
template <typename...ARGS>
void Throw(ARGS&&...args) {
std::stringstream ss;
ThrowInternal(ss, std::forward<ARGS>(args)...);
}
// ForEach tuple
template <size_t CUR, size_t END>
struct ForEachIter_t {
template <typename CALLABLE, typename...TYPES>
static void Do(const std::tuple<TYPES...>& tpl, CALLABLE&& func) {
func(std::get<CUR>(tpl));
ForEachIter_t<CUR + 1, END>::Do(tpl, func);
}
template <typename CALLABLE, typename...TYPES>
static void Do(std::tuple<TYPES...>& tpl, CALLABLE&& func) {
func(std::get<CUR>(tpl));
ForEachIter_t<CUR + 1, END>::Do(tpl, func);
}
};
template <size_t CUR>
struct ForEachIter_t<CUR, CUR> {
template <typename CALLABLE, typename...TYPES>
static void Do(const std::tuple<TYPES...>&, CALLABLE&&) {};
};
template <typename CALLABLE, typename...TYPES>
void ForEach(const std::tuple<TYPES...>& tpl, CALLABLE&& func) {
constexpr size_t tplSize = std::tuple_size<std::tuple<TYPES...>>::value;
ForEachIter_t<0, tplSize>::Do(tpl, func);
}
template <typename CALLABLE, typename...TYPES>
void ForEach(std::tuple<TYPES...>& tpl, CALLABLE&& func) {
constexpr size_t tplSize = std::tuple_size<std::tuple<TYPES...>>::value;
ForEachIter_t<0, tplSize>::Do(tpl, func);
}
// Expand Tuple to function
template <typename...TYPES, typename CALLABLE, size_t...IDX>
void ExpandHelper(const std::tuple<TYPES...>& tpl, CALLABLE&& func, std::index_sequence<IDX...>&& idx) {
func(std::get<IDX>(tpl)...);
}
template <typename...TYPES, typename CALLABLE>
void ExpandAndCall(const std::tuple<TYPES...>& tpl, CALLABLE&& func) {
ExpandHelper(tpl, std::forward<CALLABLE>(func), std::make_index_sequence<sizeof...(TYPES)>());
}
// Callbacks
namespace fn {
inline void sum(int a, int b) {
std::cout << a << " + " << b << " = " << a + b << '\n';
}
inline void div(const double& a, const double& b) {
std::cout << a << " / " << b << " = " << a / b << '\n';
}
inline void square(const unsigned long& a) {
std::cout << a << " ^ 2 = " << a * a << '\n';
}
inline void reverse(const std::string& str) {
std::cout << str << " -> " << std::string{ str.rbegin(), str.rend() } << '\n';
}
inline void con_exit() {
exit(0);
}
}
// Converter
template <typename T>
T Converter(const std::string&) {
static_assert(std::is_same<T, void>::value, "Converter for this type is not specialized");
return T{};
}
template<>
inline int Converter<int>(const std::string& str) {
try {
return stoi(str);
}
catch (...) {
Throw("Unable to convert '", str, "' to int");
}
}
template <>
inline double Converter<double>(const std::string& str) {
try {
return stod(str);
}
catch (...) {
Throw("Unable to convert '", str, "' to double");
}
}
template<>
inline unsigned long Converter<unsigned long>(const std::string& str) {
try {
if (str.front() == '-') throw std::exception{};
return stoul(str);
}
catch (...) {
Throw("Unable to convert '", str, "' to unsigned long");
}
}
template<>
inline std::string Converter<std::string>(const std::string& str) {
return str;
}
// Invoke
template<typename...ARGS>
void Invoke(void(*func)(ARGS...args), std::vector<std::string>::const_iterator curr) {
std::tuple<std::decay_t<ARGS>...> tempTpl;
ForEach(tempTpl, [&curr](auto& elem) {
elem = Converter<std::decay_t<decltype(elem)>>(*(++curr));
});
ExpandAndCall(tempTpl, func);
}
// Command
template <typename CALLABLE, typename...TYPES>
class Command_t {
template <typename...ARGS>
friend auto MakeCommand(std::string&& name, void(*func)(ARGS...));
public:
const std::string& GetName() const {
return m_name;
}
const CALLABLE& GetFunc() const {
return m_func;
}
private:
Command_t(std::string&& name, CALLABLE func) :
m_name(std::forward<std::string>(name)), m_func(func) {}
const std::string m_name;
const CALLABLE m_func;
};
template <typename...ARGS>
auto MakeCommand(std::string&& name, void(*func)(ARGS...)) {
return Command_t<decltype(func), std::decay_t<ARGS>...>(move(name), func);
}
// Branch
template <typename...CMDS>
class Branch_t {
template <typename...ARGS>
friend Branch_t<ARGS...> MakeBranch(std::string&& name, ARGS&&...args);
public:
const std::string& GetName() const {
return m_name;
}
const std::tuple<CMDS...>& GetChildren() const {
return m_cmds;
}
private:
Branch_t(std::string&& name, CMDS&&...cmds) :
m_name(std::forward<std::string>(name)),
m_cmds(std::forward_as_tuple(cmds...)) {};
const std::string m_name;
const std::tuple<CMDS...> m_cmds;
};
template <typename...ARGS>
Branch_t<ARGS...> MakeBranch(std::string&& name, ARGS&&...args) {
return Branch_t<ARGS...>(std::forward<std::string>(name), std::forward<ARGS>(args)...);
}
template <typename CALLABLE, typename...TYPES>
bool ProcessTupleElem(std::vector<std::string>::const_iterator curr, std::vector<std::string>::const_iterator last, const Command_t<CALLABLE, TYPES...>& command) {
if (command.GetName() == *curr) {
if (std::distance(curr, last) != sizeof...(TYPES)+1) {
Throw("Excepted ", sizeof...(TYPES), " arguments, got ", std::distance(curr, last) - 1);
}
Invoke(command.GetFunc(), curr);
return true;
}
return false;
}
template <typename...ARGS>
bool ProcessTupleElem(std::vector<std::string>::const_iterator curr, std::vector<std::string>::const_iterator last, const Branch_t<ARGS...>& branch) {
if (branch.GetName() == *curr) {
if (std::distance(curr, last) == 1) {
Throw("Excepted command after branch, got none");
}
bool found{ false };
ForEach(branch.GetChildren(), [&found, &curr, &last](const auto &elem) {
found |= ProcessTupleElem(curr + 1, last, elem);
});
if (!found) {
Throw("command '", *curr, "' not found in branch '", *(curr + 1), "'");
}
return true;
}
return false;
}
template <typename...ARGS>
void MagicStartsHere(const std::tuple<ARGS...>& cmds, std::vector<std::string>&& tokens) {
if (tokens.empty()) return;
bool found{ false };
ForEach(cmds, [&found, &tokens](auto&& elem) {
found |= ProcessTupleElem(tokens.cbegin(), tokens.cend(), elem);
});
if (!found) {
Throw("command '", tokens.front(), "' not found");
}
}
int main() {
const auto commands = std::make_tuple(
MakeBranch("math",
MakeCommand("sum", fn::sum),
MakeCommand("div", fn::div),
MakeCommand("square", fn::square)
),
MakeCommand("reverse", fn::reverse),
MakeCommand("exit", fn::con_exit)
);
while (true) {
std::cout << "> ";
std::string inp;
getline(std::cin, inp);
try {
MagicStartsHere(commands, Split(inp));
}
catch (const std::exception& e) {
std::cout << "ERROR: " << e.what() << '\n';
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment