Created
October 30, 2015 13:18
-
-
Save Liareth/ed828818c190491e7817 to your computer and use it in GitHub Desktop.
This file contains 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 <stack> | |
#include <tuple> | |
#include <string> | |
#include <cstdint> | |
#include <array> | |
#include <cassert> | |
template <typename T> | |
class Maybe | |
{ | |
public: | |
Maybe(Maybe<T>&& other) = default; | |
Maybe(Maybe<T>& other) = delete; | |
Maybe<T> operator=(Maybe<T> const & other) = delete; | |
explicit Maybe() | |
: m_HasValue(false), m_Data() | |
{ | |
} | |
explicit Maybe(T&& data) | |
: m_HasValue(true), m_Data(data) | |
{ | |
} | |
T& operator*() | |
{ | |
return m_Data; | |
} | |
T extract() | |
{ | |
m_HasValue = false; | |
return std::move(m_Data); | |
} | |
operator bool() const | |
{ | |
return m_HasValue; | |
} | |
private: | |
bool m_HasValue; | |
T m_Data; | |
}; | |
constexpr uint8_t TYPE_SIZE_IN_CHARACTERS = 2; | |
static_assert(TYPE_SIZE_IN_CHARACTERS >= 1, "The type size was invalid!"); | |
enum class NWNTypes : uint8_t | |
{ | |
INT = 0, | |
FLOAT, | |
OBJECT, | |
STRING = 11, // Double character test! | |
COUNT, | |
LIMIT = 100, // Limited to two characters -- e.g. up to 99 | |
// Could delimit by a space any have a scaling count (up to type size), | |
// but this is slower as it requires an allocation in ExtractType. | |
}; | |
NWNTypes ExtractType(std::string const& data) | |
{ | |
std::array<char, TYPE_SIZE_IN_CHARACTERS + 1> buffer; // + 1 for null term | |
memcpy(buffer.data(), data.c_str(), TYPE_SIZE_IN_CHARACTERS); | |
buffer[TYPE_SIZE_IN_CHARACTERS] = '\0'; | |
return static_cast<NWNTypes>(std::strtol(buffer.data(), nullptr, 10)); | |
} | |
template <typename T, typename = std::enable_if<std::is_pointer<T>::value>> | |
Maybe<T> nwnx_cast(std::string&& data) | |
{ | |
using MaybeTempl = Maybe<T>; | |
return MaybeTempl(reinterpret_cast<T>(data.c_str() + TYPE_INDEX_INTO_DATA)); | |
} | |
template <> | |
Maybe<int> nwnx_cast<int>(std::string&& data) | |
{ | |
using MaybeTempl = Maybe<int>; | |
NWNTypes const type = ExtractType(data); | |
return type == NWNTypes::INT ? MaybeTempl(std::strtol(data.c_str() + TYPE_SIZE_IN_CHARACTERS - 1, nullptr, 10)) : MaybeTempl(); | |
} | |
template <> | |
Maybe<std::string> nwnx_cast<std::string>(std::string&& data) | |
{ | |
using MaybeTempl = Maybe<std::string>; | |
NWNTypes const type = ExtractType(data); | |
data.erase(0, TYPE_SIZE_IN_CHARACTERS); // Strip the type info. | |
return type == NWNTypes::STRING ? MaybeTempl(std::forward<std::string>(data)) : MaybeTempl(); | |
} | |
template <typename T> | |
Maybe<std::tuple<T>> MakeTupleFromArgs(std::stack<std::string>& arguments) | |
{ | |
using MaybeTempl = Maybe<std::tuple<T>>; | |
auto data = nwnx_cast<T>(std::move(arguments.top())); | |
arguments.pop(); | |
return data ? MaybeTempl(std::make_tuple(data.extract())) : MaybeTempl(); | |
} | |
template <typename T1, typename T2, typename ... Ts> | |
Maybe<std::tuple<T1, T2, Ts ...>> MakeTupleFromArgs(std::stack<std::string>& arguments) | |
{ | |
using MaybeTempl = Maybe<std::tuple<T1, T2, Ts ...>>; | |
auto first = MakeTupleFromArgs<T1>(arguments); | |
auto others = MakeTupleFromArgs<T2, Ts...>(arguments); | |
return first && others ? MaybeTempl(std::tuple_cat(first.extract(), others.extract())) : MaybeTempl(); | |
} | |
template <typename ... Params> | |
Maybe<std::tuple<Params...>> ExtractArgs(std::stack<std::string>& arguments) | |
{ | |
using MaybeTempl = Maybe<std::tuple<Params...>>; | |
constexpr size_t paramCount = sizeof...(Params); | |
if (arguments.size() != paramCount) | |
{ | |
return MaybeTempl(); | |
} | |
auto data = MakeTupleFromArgs<Params...>(arguments); | |
assert(arguments.empty()); // Logically, arguments should now be empty. | |
return MaybeTempl(std::move(data)); | |
} | |
int main() | |
{ | |
std::stack<std::string> arguments; | |
arguments.push("0 0"); | |
arguments.push("0 1"); | |
arguments.push("1 2"); // Type wrong. | |
arguments.push("0 3"); | |
auto data = ExtractArgs<int, int, int, int>(arguments); // Fails because type wrong. | |
auto data2 = ExtractArgs<int, int, int, int>(arguments); // Fails because argument count invalid. | |
arguments.push("0 0"); | |
arguments.push("0 1"); | |
arguments.push("0 2"); | |
auto data3 = ExtractArgs<int, int, int>(arguments); // Succeeds. | |
arguments.push("11HelloThisIsATest"); | |
arguments.push("11IsTheMicOn?"); | |
arguments.push("0 5"); | |
arguments.push("11Bottles of beers on the wall!!!"); | |
auto data4 = ExtractArgs<std::string, int, std::string, std::string>(arguments); // Note the order. | |
// The last pushed to the stack = the first consumed in the template list. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment