-
Notifications
You must be signed in to change notification settings - Fork 227
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
10,037 additions
and
47 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (C) 2022 TiltedPhoques SRL. | ||
// For licensing information see LICENSE at the root of this distribution. | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <functional> | ||
#include <any> | ||
#include <charconv> | ||
#include <vector> | ||
|
||
namespace base | ||
{ | ||
// CommandNode | ||
// CommandData | ||
|
||
using ArgStack = std::vector<std::any>; | ||
|
||
class CommandBase | ||
{ | ||
friend class CommandRegistry; | ||
|
||
public: | ||
enum class Type | ||
{ | ||
kBoolean, | ||
kNumeric, | ||
kPointer, | ||
kVoid | ||
|
||
// I think we should introduce signed_numeric | ||
// and unsigned numeric | ||
}; | ||
|
||
CommandBase(CommandBase*& parent, const char* n, const char* d, size_t argc) | ||
: m_name(n), m_desc(d), m_argCount(argc), next(parent) | ||
{ | ||
parent = this; | ||
} | ||
|
||
explicit CommandBase(const char* n, const char* d, size_t argc) : CommandBase(ROOT(), n, d, argc) | ||
{ | ||
} | ||
|
||
//static constexpr bool IsIntegral = std::is_integral_v<bool>; | ||
|
||
protected: | ||
template <typename T> static constexpr Type ToValueTypeIndex() | ||
{ | ||
if constexpr (std::is_same_v<T, bool>) | ||
return Type::kBoolean; | ||
if constexpr (std::is_integral_v<T>) | ||
return Type::kNumeric; | ||
if constexpr (std::is_pointer_v<T>) | ||
static_assert(true, "What are you doing?"); | ||
return Type::kVoid; | ||
} | ||
|
||
private: | ||
static inline CommandBase*& ROOT() noexcept | ||
{ | ||
static CommandBase* root{nullptr}; | ||
return root; | ||
} | ||
|
||
void Invoke() | ||
{ | ||
} | ||
|
||
public: | ||
const char* m_name; | ||
const char* m_desc; | ||
|
||
// Type info | ||
const size_t m_argCount; | ||
Type* m_pArgIndicesArray = nullptr; | ||
std::function<void(const ArgStack&)> m_Handler; | ||
|
||
// Next static node. | ||
CommandBase* next = nullptr; | ||
}; | ||
|
||
template <typename... Ts> class Command : public CommandBase | ||
{ | ||
static constexpr size_t N = sizeof...(Ts); | ||
|
||
public: | ||
// Construct the command and fill the indices list at runtime. | ||
// and no, it couldnd be done with std::function | ||
template<typename TFunctor> | ||
explicit Command(const char* acName, const char* acDesc, const TFunctor& functor) | ||
: m_indices{(ToValueTypeIndex<Ts>())...}, | ||
CommandBase(acName, acDesc, N) | ||
{ | ||
CommandBase::m_pArgIndicesArray = reinterpret_cast<Type*>(&m_indices); | ||
CommandBase::m_Handler = functor; | ||
} | ||
|
||
constexpr size_t arg_count() | ||
{ | ||
return N; | ||
} | ||
|
||
private: | ||
Type m_indices[N]; | ||
}; | ||
} // namespace base |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright (C) 2022 TiltedPhoques SRL. | ||
// For licensing information see LICENSE at the root of this distribution. | ||
#pragma once | ||
|
||
#include <base/Command.h> | ||
|
||
namespace base | ||
{ | ||
namespace detail | ||
{ | ||
// SEE | ||
// https://stackoverflow.com/questions/29169153/how-do-i-verify-a-string-is-valid-double-even-if-it-has-a-point-in-it | ||
bool IsNumber(const std::string& s) | ||
{ | ||
return !s.empty() && | ||
std::find_if(s.begin(), s.end(), [](char c) { return !(std::isdigit(c) || c == '.'); }) == s.end(); | ||
} | ||
} // namespace detail | ||
|
||
// Never make this class static | ||
class CommandRegistry | ||
{ | ||
public: | ||
enum class Status | ||
{ | ||
kSuccess, | ||
kNotFound, | ||
kMissingArgs | ||
}; | ||
|
||
CommandRegistry() | ||
{ | ||
// Register global commands. | ||
for (auto* i = CommandBase::ROOT(); i;) | ||
{ | ||
m_commands.push_back(i); | ||
i = i->next; | ||
} | ||
} | ||
|
||
template <typename... Ts> void RegisterCommand(const char* name, const char* desc, std::function<void()> func) | ||
{ | ||
auto pCommand = new Command<Ts>(name, desc, func); | ||
m_commands.push_back(pCommand); | ||
} | ||
|
||
CommandBase* FindCommand(const char* acName) | ||
{ | ||
// TODO: Maybe lookup by some hash... | ||
auto it = std::find_if(m_commands.begin(), m_commands.end(), | ||
[&](CommandBase* apCommand) { return std::strcmp(apCommand->m_name, acName) == 0; }); | ||
if (it == m_commands.end()) | ||
return nullptr; | ||
return *it; | ||
} | ||
|
||
Status TryExecuteCommand(const char* acName, const std::vector<std::string>& acArgs) | ||
{ | ||
auto* pCommand = FindCommand(acName); | ||
if (!pCommand) | ||
return Status::kNotFound; | ||
|
||
// Validate arg counts against each other. | ||
if (acArgs.size() != pCommand->m_argCount) | ||
{ | ||
// Invalid | ||
|
||
} | ||
|
||
ArgStack stack; | ||
CommandBase::Type* pType = pCommand->m_pArgIndicesArray; | ||
for (size_t i = 0; i < pCommand->m_argCount; i++) | ||
{ | ||
auto& stringArg = acArgs[i]; | ||
switch (*pType) | ||
{ | ||
case CommandBase::Type::kBoolean: { | ||
if (stringArg == "true" || stringArg == "TRUE") | ||
{ | ||
stack.push_back(true); | ||
continue; | ||
} | ||
else if (stringArg == "false" || stringArg == "FALSE") | ||
{ | ||
stack.push_back(false); | ||
continue; | ||
} | ||
|
||
// Expected x got this | ||
return Status::kMissingArgs; | ||
} | ||
case CommandBase::Type::kNumeric: { | ||
if (!detail::IsNumber(stringArg)) | ||
{ | ||
// Expected argument of type numeric, got this | ||
|
||
__debugbreak(); | ||
} | ||
|
||
int64_t value = false; | ||
auto result = std::from_chars(stringArg.data(), stringArg.data() + stringArg.length(), value); | ||
if (result.ec != std::errc()) | ||
{ | ||
__debugbreak(); | ||
|
||
// Expected number, got this, number malformed | ||
return Status::kMissingArgs; | ||
} | ||
|
||
stack.push_back(value); | ||
break; | ||
} | ||
case CommandBase::Type::kVoid: | ||
case CommandBase::Type::kPointer: | ||
__debugbreak(); | ||
// Ignore. | ||
break; | ||
} | ||
pType++; | ||
} | ||
|
||
pCommand->m_Handler(stack); | ||
// Need to queue execution onto the right thread... | ||
return Status::kSuccess; | ||
} | ||
|
||
private: | ||
std::vector<CommandBase*> m_commands; | ||
|
||
// MPSC queue | ||
}; | ||
} // namespace base |
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
Oops, something went wrong.