Created
March 26, 2017 21:32
-
-
Save Starl1ght/ddf43886d405df7aae072768455f3e16 to your computer and use it in GitHub Desktop.
Console
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 <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