Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ffi: Add Value class to represent all supported primitive values for the key-value pair IR format. #502

Merged
merged 13 commits into from
Aug 8, 2024
2 changes: 2 additions & 0 deletions components/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ set(SOURCE_FILES_unitTest
src/clp/ffi/search/WildcardToken.hpp
src/clp/ffi/utils.cpp
src/clp/ffi/utils.hpp
src/clp/ffi/Value.hpp
src/clp/FileDescriptor.cpp
src/clp/FileDescriptor.hpp
src/clp/FileReader.cpp
Expand Down Expand Up @@ -464,6 +465,7 @@ set(SOURCE_FILES_unitTest
tests/test-EncodedVariableInterpreter.cpp
tests/test-encoding_methods.cpp
tests/test-ffi_SchemaTree.cpp
tests/test-ffi_Value.cpp
tests/test-Grep.cpp
tests/test-ir_encoding_methods.cpp
tests/test-ir_parsing.cpp
Expand Down
204 changes: 204 additions & 0 deletions components/core/src/clp/ffi/Value.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#ifndef CLP_FFI_VALUE_HPP
#define CLP_FFI_VALUE_HPP

#include <cstdint>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>

#include "../ErrorCode.hpp"
#include "../ir/EncodedTextAst.hpp"
#include "../TraceableException.hpp"
#include "../type_utils.hpp"

// NOTE: In this file, "primitive" doesn't refer to a C++ fundamental type (e.g. int) but instead
// refers to a value in a kv-pair that has no children (i.e. not an object/array).

LinZhihao-723 marked this conversation as resolved.
Show resolved Hide resolved
namespace clp::ffi {
using value_int_t = int64_t;
using value_float_t = double;
using value_bool_t = bool;

/**
* Tuple of all primitive value types.
*/
using PrimitiveValueTypes = std::tuple<
value_int_t,
value_float_t,
value_bool_t,
std::string,
clp::ir::EightByteEncodedTextAst,
clp::ir::FourByteEncodedTextAst>;

/**
* Variant for all primitive value types.
*/
using PrimitiveValueVariant = tuple_to_variant<PrimitiveValueTypes>::Type;

/**
* Template to validate whether the given type is a primitive value type.
* @tparam T
*/
template <typename T>
constexpr bool cIsPrimitiveValueType = is_in_type_tuple<T, PrimitiveValueTypes>::value;

/**
* Concept that defines primitive value types.
*/
template <typename T>
concept PrimitiveValueType = cIsPrimitiveValueType<T>;

/**
* Concept that defines primitive value types that are also C++ fundamental types.
*/
template <typename T>
concept FundamentalPrimitiveValueType = cIsPrimitiveValueType<T> && std::is_fundamental_v<T>;

/**
* Concept that defines move-constructable primitive value types.
*/
template <typename T>
concept MoveConstructablePrimitiveValueType
= cIsPrimitiveValueType<T> && std::is_move_constructible_v<T>
&& (false == std::is_fundamental_v<T>);

/**
* Template struct that converts a given type into an immutable view type. By default, the immutable
* view type is the given type itself, meaning that the immutable view is a copy. Specialization is
* needed when the immutable view type is a const reference or some other types.
* @tparam T
*/
template <typename T>
struct ImmutableViewTypeConverter {
using Type = T;
};

/**
* Specializes `std::string`'s immutable view type as a `std::string_view`.
*/
template <>
struct ImmutableViewTypeConverter<std::string> {
using Type = std::string_view;
};

/**
* Specializes `clp::ir::EightByteEncodedTextAst`'s immutable view type as its const reference.
*/
template <>
struct ImmutableViewTypeConverter<clp::ir::EightByteEncodedTextAst> {
using Type = clp::ir::EightByteEncodedTextAst const&;
};

/**
* Specializes `clp::ir::FourByteEncodedTextAst`'s immutable view type as its const reference.
*/
template <>
struct ImmutableViewTypeConverter<clp::ir::FourByteEncodedTextAst> {
using Type = clp::ir::FourByteEncodedTextAst const&;
};

/**
* Template alias to the underlying typename of `ImmutableViewTypeConverter`.
* @tparam T
*/
template <typename T>
using ImmutableViewType = typename ImmutableViewTypeConverter<T>::Type;

class Value {
public:
// Types
class OperationFailed : public TraceableException {
public:
OperationFailed(
ErrorCode error_code,
char const* const filename,
int line_number,
std::string message
)
: TraceableException{error_code, filename, line_number},
m_message{std::move(message)} {}

[[nodiscard]] auto what() const noexcept -> char const* override {
return m_message.c_str();
}

private:
std::string m_message;
};

// Constructors
Value() = default;

/**
* Constructs a `Value` by moving the given primitive value.
* @tparam T The type of the value, which must be an rvalue to a move-constructable primitive
* value type.
* @param value
*/
template <MoveConstructablePrimitiveValueType T>
explicit Value(T&& value) : m_value{std::forward<T>(value)} {
static_assert(std::is_rvalue_reference_v<decltype(value)>);
}

/**
* @tparam T The type of the given value, which must be a fundamental primitive value type.
* @param value
*/
template <FundamentalPrimitiveValueType T>
explicit Value(T value) : m_value{value} {}

// Disable copy constructor and assignment operator
Value(Value const&) = delete;
auto operator=(Value const&) -> Value& = delete;

// Default move constructor and assignment operator
Value(Value&&) = default;
auto operator=(Value&&) -> Value& = default;

// Destructor
~Value() = default;

// Methods
/**
* @tparam T
* @return Whether the underlying value is the given type.
*/
template <PrimitiveValueType T>
[[nodiscard]] auto is() const -> bool {
return std::holds_alternative<T>(m_value);
}

/**
* @tparam T
* @return An immutable view of the underlying value if its type matches the given type.
* @throw OperationFailed if the given type doesn't match the underlying value's type.
*/
template <PrimitiveValueType T>
[[nodiscard]] auto get_immutable_view() const -> ImmutableViewType<T> {
if (false == is<T>()) {
throw OperationFailed(
clp::ErrorCode_BadParam,
__FILE__,
__LINE__,
"The underlying value does not match the given type."
);
}
return std::get<T>(m_value);
}

/**
* @return Whether the underlying value is null.
*/
[[nodiscard]] auto is_null() const -> bool {
return std::holds_alternative<std::monostate>(m_value);
}

private:
PrimitiveValueVariant m_value{std::monostate{}};
};
} // namespace clp::ffi

#endif // CLP_FFI_VALUE_HPP
2 changes: 2 additions & 0 deletions components/core/src/clp/ir/EncodedTextAst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class EncodedTextAst {
~EncodedTextAst() = default;

// Methods
auto operator==(EncodedTextAst const&) const -> bool = default;

[[nodiscard]] auto get_logtype() const -> std::string const& { return m_logtype; }

[[nodiscard]] auto get_dict_vars() const -> std::vector<std::string> const& {
Expand Down
26 changes: 26 additions & 0 deletions components/core/src/clp/type_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#define CLP_TYPE_UTILS_HPP

#include <cstring>
#include <tuple>
#include <type_traits>
#include <variant>

namespace clp {
/**
Expand Down Expand Up @@ -67,6 +69,30 @@ std::enable_if_t<sizeof(Destination) == sizeof(Source), Destination*>
size_checked_pointer_cast(Source* src) {
return reinterpret_cast<Destination*>(src);
}

/**
* Template that converts a tuple of types into a variant.
* @tparam Tuple A tuple of types
*/
template <typename Tuple>
struct tuple_to_variant;

template <typename... Types>
struct tuple_to_variant<std::tuple<Types...>> {
using Type = std::variant<std::monostate, Types...>;
};

/**
* Template to validate if the given type is in the given tuple of types.
* @tparam Type
* @tparam Tuple
*/
template <typename Type, typename Tuple>
struct is_in_type_tuple;

template <typename Type, typename... Types>
struct is_in_type_tuple<Type, std::tuple<Types...>>
: std::disjunction<std::is_same<Type, Types>...> {};
} // namespace clp

#endif // CLP_TYPE_UTILS_HPP
Loading
Loading