Skip to content

Commit

Permalink
Refactor call method from cli/helper.h
Browse files Browse the repository at this point in the history
- Use a single template for both std::optional and non-optional types.
- Replace std::stoull with std::from_chars or std::istringstream based
  on GCC version. We should support GCC 7.5, but unfortunately it
  supports not full set of C++17 features. #include <charconv> is unsupported
- Replaced recursive template functions with a single function utilizing
  std::index_sequence and fold expressions to iterate over tuple elements.
- Add support for floating-point arguments
- Remove casting of call argument to a function pointer. Now it can
  accept any callable which is more efficient and extensive
- Rename it to Call (google codestyle)
  • Loading branch information
ol-imorozko committed Oct 1, 2024
1 parent d580254 commit 2de0ccd
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 218 deletions.
177 changes: 58 additions & 119 deletions cli/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,152 +7,91 @@
#include <vector>

#include "common/sdpclient.h"
#include "common/traits.h"
#include "table_printer.h"

void fillValue(std::optional<uint8_t>& value, const std::string& string)
{
if (string == "any")
{
value = std::nullopt;
}
else if (string != "")
{
value = std::stoull(string, nullptr, 0);
;
}
else
{
value = std::nullopt;
}
}
#if __cpp_lib_charconv >= 201606L && !defined(__GNUC__) || __GNUC__ >= 8
#define USE_FROM_CHARS
#endif

void fillValue(std::optional<uint16_t>& value, const std::string& string)
{
if (string == "any")
{
value = std::nullopt;
}
else if (string != "")
{
value = std::stoull(string, nullptr, 0);
;
}
else
{
value = std::nullopt;
}
}
#if defined(USE_FROM_CHARS)
#include <charconv>
#else
#include <sstream>
#endif

void fillValue(std::optional<uint32_t>& value, const std::string& string)
template<typename T>
static void fill(T& value, const std::string& str)
{
if (string == "any")
{
value = std::nullopt;
}
else if (string != "")
if constexpr (std::is_same_v<T, bool>)
{
value = std::stoull(string, nullptr, 0);
;
}
else
{
value = std::nullopt;
}
}

template<typename TArg>
void fillValue(std::optional<TArg>& value, const std::string& string)
{
if (string == "any")
{
value = std::nullopt;
if (str == "true")
value = true;
else if (str == "false")
value = false;
else
throw std::invalid_argument("Invalid boolean value");
}
else if (string != "")
else if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>)
{
value = string;
#ifdef USE_FROM_CHARS
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
if (ec != std::errc{})
throw std::invalid_argument("Invalid numeric value: '" + str + "'");
#else
std::istringstream iss(str);
if (!(iss >> value) || !iss.eof())
throw std::invalid_argument("Invalid numeric value: '" + str + "'");
#endif
}
else
else if constexpr (traits::is_optional_v<T>)
{
value = std::nullopt;
}
}

void fillValue(bool& value, const std::string& string)
{
if (string == "false" || string == "true")
{
value = string == "true";
if (str == "any" || str.empty())
{
value.reset();
}
else
{
typename T::value_type temp;
fill(temp, str);
value = std::move(temp);
}
}
else
{
throw std::string("invalid argument, must be true or false");
value = str;
}
}

void fillValue(uint8_t& value, const std::string& string)
{
value = std::stoull(string, nullptr, 0);
}

void fillValue(uint16_t& value, const std::string& string)
{
value = std::stoull(string, nullptr, 0);
}

void fillValue(uint32_t& value, const std::string& string)
// Fill a tuple with values from a vector of strings
template<typename Tuple, std::size_t... Is>
static void fillTupleImpl(Tuple& tuple, const std::vector<std::string>& args, std::index_sequence<Is...>)
{
value = std::stoull(string, nullptr, 0);
(..., fill(std::get<Is>(tuple), Is < args.size() ? args[Is] : std::string{}));
}

template<typename TArg>
void fillValue(TArg& value, const std::string& string)
template<typename... Args>
static void fillTuple(std::tuple<Args...>& tuple, const std::vector<std::string>& args)
{
value = string;
fillTupleImpl(tuple, args, std::index_sequence_for<Args...>{});
}

template<typename TArg>
void fillValue(std::optional<TArg>& value)
// Call function using string arguments
template<typename F>
inline void Call(F&& func, const std::vector<std::string>& string_args)
{
value = std::nullopt;
}

template<typename TArg>
void fillValue([[maybe_unused]] TArg& value)
{
throw std::string("invalid arguments count");
}
using ArgsTuple = typename traits::function<F>::args;
constexpr auto arity = std::tuple_size_v<ArgsTuple>;

template<size_t TIndex,
size_t TSize,
typename TTuple>
void fillTuple(TTuple& tuple, const std::vector<std::string>& stringArgs)
{
if constexpr (TIndex < TSize)
if (string_args.size() > arity)
{
if (TIndex < stringArgs.size())
{
fillValue(std::get<TIndex>(tuple), stringArgs[TIndex]);
}
else
{
fillValue(std::get<TIndex>(tuple));
}

fillTuple<TIndex + 1, TSize>(tuple, stringArgs);
throw std::invalid_argument("Invalid arguments count: '" + std::to_string(string_args.size()) +
"', expected at most: '" + std::to_string(arity) + "'");
}
}

template<typename... TArgs>
void call(void (*func)(TArgs... args),
const std::vector<std::string>& stringArgs)
{
if (stringArgs.size() > sizeof...(TArgs))
{
throw std::string("invalid arguments count: '") + std::to_string(stringArgs.size()) + "', need: '" + std::to_string(sizeof...(TArgs)) + "'";
}
std::tuple<std::decay_t<TArgs>...> tuple;
fillTuple<0, sizeof...(TArgs)>(tuple, stringArgs);
std::apply(func, tuple);
utils::decay_tuple<ArgsTuple> args_tuple;
fillTuple(args_tuple, string_args);
std::apply(std::forward<F>(func), args_tuple);
}

void OpenSharedMemoryDataplaneBuffers(common::sdp::DataPlaneInSharedMemory& sdp_data, bool open_workers_data)
Expand Down
Loading

0 comments on commit 2de0ccd

Please sign in to comment.