Ver a proveniência

* Initial commit (implemented basic RTTI for C++)

master
bergmann há 5 anos
cometimento
55443c38a4
45 ficheiros alterados com 2507 adições e 0 eliminações
  1. +3
    -0
      .gitmodules
  2. +6
    -0
      .vscode/settings.json
  3. +63
    -0
      CMakeLists.txt
  4. +34
    -0
      cmake/Findcppcore.cmake
  5. +34
    -0
      cmake/Findcppmp.cmake
  6. +9
    -0
      cmake/cpprtti-config.cmake
  7. +28
    -0
      cmake/cpprtti-const.cmake
  8. +23
    -0
      cmake/cpprtti-options.cmake
  9. +26
    -0
      cmake/cpprtti-var.cmake
  10. +1
    -0
      cmake/modules
  11. +6
    -0
      include/cpprtti.h
  12. +9
    -0
      include/cpprtti/misc.h
  13. +76
    -0
      include/cpprtti/misc/exception.h
  14. +81
    -0
      include/cpprtti/misc/exception.inl
  15. +25
    -0
      include/cpprtti/misc/helper.h
  16. +43
    -0
      include/cpprtti/misc/helper.inl
  17. +102
    -0
      include/cpprtti/misc/variant.h
  18. +256
    -0
      include/cpprtti/misc/variant.inl
  19. +96
    -0
      include/cpprtti/registry.h
  20. +140
    -0
      include/cpprtti/registry.inl
  21. +5
    -0
      include/cpprtti/traits.h
  22. +14
    -0
      include/cpprtti/traits/default_type.h
  23. +27
    -0
      include/cpprtti/traits/default_type.inl
  24. +14
    -0
      include/cpprtti/types.h
  25. +55
    -0
      include/cpprtti/types/class_type.h
  26. +71
    -0
      include/cpprtti/types/class_type.inl
  27. +25
    -0
      include/cpprtti/types/class_type_tpl.h
  28. +20
    -0
      include/cpprtti/types/class_type_tpl.inl
  29. +23
    -0
      include/cpprtti/types/fundamental_type.h
  30. +17
    -0
      include/cpprtti/types/fundamental_type.inl
  31. +25
    -0
      include/cpprtti/types/fundamental_type_tpl.h
  32. +19
    -0
      include/cpprtti/types/fundamental_type_tpl.inl
  33. +9
    -0
      include/cpprtti/types/member.h
  34. +56
    -0
      include/cpprtti/types/member/member.h
  35. +28
    -0
      include/cpprtti/types/member/member.inl
  36. +46
    -0
      include/cpprtti/types/member/member_method.h
  37. +113
    -0
      include/cpprtti/types/member/member_method.inl
  38. +79
    -0
      include/cpprtti/types/member/member_variable.h
  39. +387
    -0
      include/cpprtti/types/member/member_variable.inl
  40. +75
    -0
      include/cpprtti/types/type.h
  41. +36
    -0
      include/cpprtti/types/type.inl
  42. +38
    -0
      src/CMakeLists.txt
  43. +52
    -0
      test/CMakeLists.txt
  44. +101
    -0
      test/cpprtti/misc/variant_tests.cpp
  45. +111
    -0
      test/cpprtti/type/class_type_tests.cpp

+ 3
- 0
.gitmodules Ver ficheiro

@@ -0,0 +1,3 @@
[submodule "cmake/modules"]
path = cmake/modules
url = https://git.bergmann89.de/cpp/CmakeModules.git

+ 6
- 0
.vscode/settings.json Ver ficheiro

@@ -0,0 +1,6 @@
{
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"files.insertFinalNewline": true,
"files.eol": "\n"
}

+ 63
- 0
CMakeLists.txt Ver ficheiro

@@ -0,0 +1,63 @@
# Initialize CMake ################################################################################

CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR )

# Set CMAKE_BUILD_TYPE
If ( NOT CMAKE_BUILD_TYPE )
Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE )
EndIf ( NOT CMAKE_BUILD_TYPE )
Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel )

# Set CMAKE_MODULE_PATH
If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
EndIf ( )
If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" )
EndIf ( )

# Project #########################################################################################

Include ( GNUInstallDirs )
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-options.cmake )
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-const.cmake )
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-var.cmake )
Project ( ${CPPRTTI_PROJECT_NAME}
DESCRIPTION "${CPPRTTI_PROJECT_DESCRIPTION}"
VERSION "${CPPRTTI_VERSION}" )
Include ( CTest )

# Subdirectories
Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src )
Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test )

# Install
If ( NOT CPPRTTI_HAS_EXPORT
OR NOT CPPRTTI_INSTALL_PACKAGE )
Return ( )
EndIf ( )

Include ( CMakePackageConfigHelpers )
Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake"
VERSION ${CPPRTTI_VERSION}
COMPATIBILITY AnyNewerVersion )
Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake"
@ONLY )

Set ( ConfigPackageLocation "${CPPRTTI_INSTALL_DIR_SHARE}/cmake" )
Install ( EXPORT
cpprtti
NAMESPACE
cpprtti::
DESTINATION
${ConfigPackageLocation} )
Install ( FILES
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake"
DESTINATION
${ConfigPackageLocation}
COMPONENT
Devel )

+ 34
- 0
cmake/Findcppcore.cmake Ver ficheiro

@@ -0,0 +1,34 @@
Set ( CPPCORE_REPOSITORY https://git.bergmann89.de/cpp/cppcore.git )
Set ( CPPCORE_COMMIT master )
Set ( CPPCORE_CHECKOUT_DIR ${CMAKE_BINARY_DIR}/sources/cppcore )

# Clone Repository
If ( NOT EXISTS ${CPPCORE_CHECKOUT_DIR} )
Execute_Process ( COMMAND
git clone ${CPPCORE_REPOSITORY} ${CPPCORE_CHECKOUT_DIR}
RESULT_VARIABLE
CLONE_RET )
If ( NOT ${CLONE_RET} EQUAL 0 )
Return ( )
EndIf ( )
EndIf ( )

# Checkout Specific Commit
Execute_Process ( COMMAND
git -C ${CPPCORE_CHECKOUT_DIR} -c advice.detachedHead=false checkout origin/${CPPCORE_COMMIT}
COMMAND
git -C ${CPPCORE_CHECKOUT_DIR} submodule update --init --recursive
RESULT_VARIABLE
CHECKOUT_RET )
If ( NOT ${CHECKOUT_RET} EQUAL 0 )
Return ( )
EndIf ( )

# Include Cloned Repository
Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake )
FindLocalModule ( cppcore ${CPPCORE_CHECKOUT_DIR} )

# Create Alias Targets
If ( NOT TARGET cppcore::cppcore )
Add_Library ( cppcore::cppcore ALIAS cppcore )
EndIf ( )

+ 34
- 0
cmake/Findcppmp.cmake Ver ficheiro

@@ -0,0 +1,34 @@
Set ( CPPMP_REPOSITORY https://git.bergmann89.de/cpp/cppmp.git )
Set ( CPPMP_COMMIT master )
Set ( CPPMP_CHECKOUT_DIR ${CMAKE_BINARY_DIR}/sources/cppmp )

# Clone Repository
If ( NOT EXISTS ${CPPMP_CHECKOUT_DIR} )
Execute_Process ( COMMAND
git clone ${CPPMP_REPOSITORY} ${CPPMP_CHECKOUT_DIR}
RESULT_VARIABLE
CLONE_RET )
If ( NOT ${CLONE_RET} EQUAL 0 )
Return ( )
EndIf ( )
EndIf ( )

# Checkout Specific Commit
Execute_Process ( COMMAND
git -C ${CPPMP_CHECKOUT_DIR} -c advice.detachedHead=false checkout origin/${CPPMP_COMMIT}
COMMAND
git -C ${CPPMP_CHECKOUT_DIR} submodule update --init --recursive
RESULT_VARIABLE
CHECKOUT_RET )
If ( NOT ${CHECKOUT_RET} EQUAL 0 )
Return ( )
EndIf ( )

# Include Cloned Repository
Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake )
FindLocalModule ( cppmp ${CPPMP_CHECKOUT_DIR} )

# Create Alias Targets
If ( NOT TARGET cppmp::cppmp )
Add_Library ( cppmp::cppmp ALIAS cppmp )
EndIf ( )

+ 9
- 0
cmake/cpprtti-config.cmake Ver ficheiro

@@ -0,0 +1,9 @@
# cpprtti-config.cmake - package configuration file

# Include ( CMakeFindDependencyMacro )
# Find_Dependency ( <dependency> )

Include ( FindPackageHandleStandardArgs )
Set ( ${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE} )
Find_Package_Handle_Standard_Args ( cpprtti CONFIG_MODE )
Include ( "${CMAKE_CURRENT_LIST_DIR}/cpprtti.cmake")

+ 28
- 0
cmake/cpprtti-const.cmake Ver ficheiro

@@ -0,0 +1,28 @@
# This file contains constant variables that are fixed to this project

# Version
Set ( CPPRTTI_VERSION_MAJOR 1 )
Set ( CPPRTTI_VERSION_MINOR 0 )
Set ( CPPRTTI_VERSION_PATCH 0 )
Set ( CPPRTTI_VERSION_BUILD 0 )
Set ( CPPRTTI_VERSION_HASH "" )
Set ( CPPRTTI_VERSION_BEHIND 0 )
Set ( CPPRTTI_VERSION_DIRTY 0 )

# Names
Set ( CPPRTTI_PROJECT_NAME "cpprtti" )
Set ( CPPRTTI_PROJECT_DESCRIPTION "A simple hello world library" )

# Include generated variables for further usage
Include ( ${CMAKE_CURRENT_LIST_DIR}/cpprtti-var.cmake )

# Install directories
Set ( CPPRTTI_INSTALL_DIR_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}/${CPPRTTI_NAME}" )
Set ( CPPRTTI_INSTALL_DIR_LIB "${CMAKE_INSTALL_LIBDIR}" )
Set ( CPPRTTI_INSTALL_DIR_SHARE "${CMAKE_INSTALL_DATAROOTDIR}/${CPPRTTI_NAME}" )

# C Standard
Set ( CMAKE_C_STANDARD 11 )
Set ( CMAKE_CXX_STANDARD 17 )
Set ( CMAKE_C_STANDARD_REQUIRED ON )
Set ( CMAKE_CXX_STANDARD_REQUIRED ON )

+ 23
- 0
cmake/cpprtti-options.cmake Ver ficheiro

@@ -0,0 +1,23 @@
# This file contains options that can be passed to the cmake command

Option ( CPPRTTI_INSTALL_HEADER
"Install headers of cpprtti."
ON )
Option ( CPPRTTI_INSTALL_STATIC
"Install static library of cpprtti."
ON )
Option ( CPPRTTI_INSTALL_SHARED
"Install shared library of cpprtti."
ON )
Option ( CPPRTTI_INSTALL_DEBUG
"Install the stripped debug informations of cpprtti."
OFF )
Option ( CPPRTTI_INSTALL_PACKAGE
"Install the cmake package of cpprtti."
ON )
Option ( CPPRTTI_NO_STRIP
"Do not strip debug symbols from binary."
OFF )
Option ( CPPRTTI_USE_GIT_VERSION
"Read the git tags to get the version of cpprtti"
ON )

+ 26
- 0
cmake/cpprtti-var.cmake Ver ficheiro

@@ -0,0 +1,26 @@
# This file contains generated variables that are needed for the project

# Git Version
If ( CPPRTTI_USE_GIT_VERSION )
Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER )
If ( HAS_GIT_HELPER )
GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/..
CPPRTTI_VERSION_MAJOR
CPPRTTI_VERSION_MINOR
CPPRTTI_VERSION_PATCH
CPPRTTI_VERSION_BUILD
CPPRTTI_VERSION_HASH
CPPRTTI_VERSION_BEHIND
CPPRTTI_VERSION_DIRTY )
EndIf ( )
EndIf ( )

# Strings
Set ( CPPRTTI_VERSION_SHORT "${CPPRTTI_VERSION_MAJOR}.${CPPRTTI_VERSION_MINOR}" )
Set ( CPPRTTI_VERSION "${CPPRTTI_VERSION_SHORT}.${CPPRTTI_VERSION_PATCH}.${CPPRTTI_VERSION_BUILD}" )
Set ( CPPRTTI_VERSION_COMPLETE "${CPPRTTI_VERSION}" )
Set ( CPPRTTI_NAME "${CPPRTTI_PROJECT_NAME}-${CPPRTTI_VERSION_SHORT}" )
Set ( CPPRTTI_OUTPUTNAME "${CPPRTTI_PROJECT_NAME}" )
If ( CPPRTTI_VERSION_BEHIND )
Set ( CPPRTTI_VERSION_COMPLETE "${CPPRTTI_VERSION_COMPLETE}+${CPPRTTI_VERSION_BEHIND}" )
EndIf ( )

+ 1
- 0
cmake/modules

@@ -0,0 +1 @@
Subproject commit ebbae4fbb42d671331b4c6e9d1d142f41dcacc1b

+ 6
- 0
include/cpprtti.h Ver ficheiro

@@ -0,0 +1,6 @@
#pragma once

#include <cpprtti/misc.h>
#include <cpprtti/registry.h>
#include <cpprtti/traits.h>
#include <cpprtti/types.h>

+ 9
- 0
include/cpprtti/misc.h Ver ficheiro

@@ -0,0 +1,9 @@
#pragma once

#include "misc/exception.h"
#include "misc/helper.h"
#include "misc/variant.h"

#include "misc/exception.inl"
#include "misc/helper.inl"
#include "misc/variant.inl"

+ 76
- 0
include/cpprtti/misc/exception.h Ver ficheiro

@@ -0,0 +1,76 @@
#pragma once

#include <string>

#include <cppcore/misc/exception.h>

namespace cpprtti
{

enum class error_code
{
unknown = -1,

none = 0,

/* registry */
registry_error = 1000,
registry_type_not_found, //!< Unable to find the requested type.
registry_id_already_exsists, //!< Unable to create type: The type ID does already exist!
registry_name_already_exsists, //!< Unable to create type: The type name does already exist!
registry_type_does_not_match, //!< Unable to get type: The implementation of the type does not match!

/* variant */
variant_error = 2000,
variant_is_empty, //!< The variant does not store any value!
variant_type_mismatch, //!< The variant does not store the requested type!
variant_invalid_cast, //!< The value stored in the variant could not be cast to the requested type!

/* class */
class_error = 3000,
class_member_already_exists, //!< Unable to create member: Name is already in use!
class_type_mismatch, //!< Type does not match the.
class_member_not_readable, //!< Member variable is not readable!
class_member_not_writable, //!< Member variable is not writable!
};

/**
* @brief Get the string representation of the passed error code.
*/
inline std::string get_error_code_str(error_code err);

/**
* @brief Exception to represent curl errors.
*/
struct exception
: public ::cppcore::exception
{
public:
error_code error;
const std::string detail;

/**
* @brief Constructor.
*/
inline exception(
error_code p_error);

/**
* @brief Constructor.
*/
inline exception(
error_code p_error,
const std::string& p_detail);

protected:
/**
* @brief Print the message of the exception to the passed stream.
*
* @param[in] os Stream to print message of the exception to.
*/
inline void print_message(std::ostream& os) const override;
};

}

#include "exception.inl"

+ 81
- 0
include/cpprtti/misc/exception.inl Ver ficheiro

@@ -0,0 +1,81 @@
#pragma once

#include <cppcore/conversion/enum.h>
#include <cppcore/conversion/convert_cast.h>

#include "exception.h"

namespace cpprtti
{

struct error_code_enum_values
{
using enum_type = error_code;
using enum_value_pair_type = std::pair<enum_type, std::string>;
using enum_value_vector_type = std::vector<enum_value_pair_type>;

static const enum_value_vector_type& get_enum_values()
{
static const enum_value_vector_type values({
{ error_code::unknown, "Unknown error" },

{ error_code::none, "No error" },

/* registry */
{ error_code::registry_error, "Registry error" },
{ error_code::registry_type_not_found, "Unable to find the requested type!" },
{ error_code::registry_id_already_exsists, "Unable to create type: The type ID does already exist!" },
{ error_code::registry_name_already_exsists, "Unable to create type: The type name does already exist!" },
{ error_code::registry_type_does_not_match, "Unable to get type: The implementation of the type does not match!" },

/* variant */
{ error_code::variant_error, "Variant error" },
{ error_code::variant_is_empty, "The variant does not store any value!" },
{ error_code::variant_type_mismatch, "The variant does not store the requested type!" },
{ error_code::variant_invalid_cast, "The value stored in the variant could not be cast to the requested type!" },

/* class */
{ error_code::class_error, "Class error" },
{ error_code::class_member_already_exists, "Unable to create member: Name is already in use!" },
{ error_code::class_type_mismatch, "Type does not match the." },
{ error_code::class_member_not_readable, "Member variable is not readable!" },
{ error_code::class_member_not_writable, "Member variable is not writable!" },
});
return values;
}
};

using error_code_enum_conversion_traits = ::cppcore::enum_conversion_traits<error_code, error_code_enum_values>;
using error_code_enum_conversion = ::cppcore::enum_conversion <error_code, error_code_enum_conversion_traits>;

std::string get_error_code_str(error_code err)
{ return error_code_enum_conversion::to_string(err); }

/* exception */

exception::exception(
error_code p_error)
: cppcore::exception()
, error (p_error)
, detail ()
{ }

exception::exception(
error_code p_error,
const std::string& p_detail)
: cppcore::exception(p_detail)
, error (p_error)
, detail (p_detail)
{ }

void exception
::print_message(std::ostream& os) const
{
os << get_error_code_str(error)
<< '('
<< cppcore::convert_cast<std::underlying_type_t<error_code>>(error)
<< "): "
<< message;
}

}

+ 25
- 0
include/cpprtti/misc/helper.h Ver ficheiro

@@ -0,0 +1,25 @@
#pragma once

#include <map>
#include <memory>

namespace cpprtti
{

using type_id = size_t;

/**
* @brief Get the ID of a certain C++ type.
*/
template<typename X>
inline type_id get_type_id();

/**
* @brief Get the name of a certain C++ type.
*/
template<typename X>
inline const std::string& get_type_name();

}

#include "helper.inl"

+ 43
- 0
include/cpprtti/misc/helper.inl Ver ficheiro

@@ -0,0 +1,43 @@
#pragma once

#include <cxxabi.h>

#include "helper.h"

namespace cpprtti
{

namespace __impl
{

inline type_id create_type_id()
{
static type_id value { };
return ++value;
}

template<typename X>
inline std::string create_type_name()
{
int status;
auto name = abi::__cxa_demangle(typeid(X).name(), nullptr, nullptr, &status);
return std::string(name ? name : typeid(X).name());
}

}

template<typename X>
type_id get_type_id()
{
static const auto value = __impl::create_type_id();
return value;
}

template<typename X>
const std::string& get_type_name()
{
static const auto value = __impl::create_type_name<X>();
return value;
}

}

+ 102
- 0
include/cpprtti/misc/variant.h Ver ficheiro

@@ -0,0 +1,102 @@
#pragma once

#include <memory>

#include <cppmp/misc/generic_predicate.h>

#include "helper.h"
#include "../types/type.h"

namespace cpprtti
{

namespace __impl
{

/* make_variant_impl */

template<typename X, typename = void>
struct variant_builder;

/* variant_impl */

struct variant_impl
{
const type& type;
void * data { nullptr };
bool is_const { false };
bool is_reference { false };

/**
* @brief Constructor.
*/
inline variant_impl(const cpprtti::type& p_type);
};

using variant_impl_ptr_s = std::shared_ptr<const variant_impl>;

}

struct variant
{
public:
template<typename X, typename>
friend struct __impl::variant_builder;

private:
__impl::variant_impl_ptr_s _impl; //!< Implementation of the variant

/**
* @brief Constructor.
*/
inline variant(__impl::variant_impl_ptr_s p_impl);

public:
/**
* @brief Default constructor.
*/
inline variant();

/**
* @brief Get type information of the stored value.
*/
inline const type& type() const;

/**
* @brief Check if the stored type is a reference.
*/
inline bool is_reference() const;

/**
* @brief Check if the stored type is a constant.
*/
inline bool is_const() const;

/**
* @brief Get the stored value.
*/
template<typename T>
inline T get() const;

/**
* @brief Check if the variant contains a value.
*/
inline bool empty();

/**
* @brief Check if the variant contains a value.
*/
inline operator bool() const;

private:
/**
* @brief Check if the variant has a value assigned, if not throw an exception.
*/
inline void check_impl() const;
};

constexpr decltype(auto) make_variant = cppmp::generic_predicate<__impl::variant_builder> { };

}

#include "variant.inl"

+ 256
- 0
include/cpprtti/misc/variant.inl Ver ficheiro

@@ -0,0 +1,256 @@
#pragma once

#include <cppcore/misc/string_builder.h>

#include <cppmp/core/checker.h>
#include <cppmp/core/modifier.h>
#include <cppmp/core/conditionals.h>
#include <cppmp/misc/generic_predicate.h>

#include "variant.h"
#include "exception.h"
#include "../registry.h"

namespace cpprtti
{

namespace __impl
{

/* variant_impl */

variant_impl::variant_impl(
const cpprtti::type& p_type)
: type(p_type)
{ }

/* variant_builder - default */

template<typename X, typename>
struct variant_builder
{
using is_default = cppmp::true_t;

template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&...)
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_variant(...)!"); }
};

/* variant_builder - not lvalue reference */

template<typename T_registry, typename T>
struct variant_builder<
cppmp::list<T_registry, T>,
cppmp::enable_if_t<
cppmp::is_same_v<cppmp::decay_t<T_registry>, registry>
&& !cppmp::is_lvalue_reference_v<T>
>
>
{
struct impl
: public variant_impl
{
T storage;

template<typename X>
inline impl(const cpprtti::type& p_type, X&& x)
: variant_impl (p_type)
, storage (std::forward<X>(x))
{
data = reinterpret_cast<void*>(const_cast<cppmp::decay_t<T>*>(&storage));
is_const = cppmp::is_const_v<cppmp::remove_reference_t<T>>;
is_reference = false;
}
};

template<typename X_registry, typename X>
static constexpr decltype(auto) apply(X_registry& p_registry, X&& x)
{
return variant(std::make_shared<impl>(
p_registry.template get<cppmp::decay_t<T>>(),
std::forward<X>(x)));
}
};

/* variant_builder - lvalue reference */

template<typename T_registry, typename T>
struct variant_builder<
cppmp::list<T_registry, T>,
cppmp::enable_if_t<
cppmp::is_same_v<cppmp::decay_t<T_registry>, registry>
&& cppmp::is_lvalue_reference_v<T>
>
>
{
struct impl
: public variant_impl
{
template<typename X>
inline impl(const cpprtti::type& p_type, X&& x)
: variant_impl(p_type)
{
data = reinterpret_cast<void*>(const_cast<cppmp::decay_t<T>*>(&x));
is_const = cppmp::is_const_v<cppmp::remove_reference_t<T>>;
is_reference = true;
}
};

template<typename X_registry, typename X>
static constexpr decltype(auto) apply(X_registry& p_registry, X&& x)
{
return variant(std::make_shared<impl>(
p_registry.template get<cppmp::decay_t<T>>(),
std::forward<X>(x)));
}
};

/* variant_builder - variant */

template<typename T_registry, typename T>
struct variant_builder<
cppmp::list<T_registry, T>,
cppmp::enable_if_t<
cppmp::is_same_v<cppmp::decay_t<T_registry>, registry>
&& cppmp::is_same_v<cppmp::decay_t<T_registry>, variant>
>
>
{
template<typename X_registry, typename X>
static constexpr decltype(auto) apply(X_registry& p_registry, X&& x)
{ return std::forward<X>(x); }
};

/* value_extractor - default */

template<typename X, typename = void>
struct value_extractor
{
using is_default = cppmp::true_t;

template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&...)
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for variant.get(...)!"); }
};

/* value_extractor - T&& */

template<typename T>
struct value_extractor<
cppmp::list<
const variant_impl&,
const cppmp::type_t<T&&>&>,
void>
{
static constexpr decltype(auto) apply(
const variant_impl& impl,
const cppmp::type_t<T&&>&)
{
if (impl.is_const)
{
throw exception(
error_code::variant_invalid_cast,
"Can not move constant value");
}
return std::move(*reinterpret_cast<T*>(impl.data));
}
};

/* value_extractor - normal */

template<typename T>
struct value_extractor<
cppmp::list<
const variant_impl&,
const cppmp::type_t<T>&>,
void>
{
static constexpr decltype(auto) apply(
const variant_impl& impl,
const cppmp::type_t<T>&)
{
bool is_const = cppmp::is_const_v<cppmp::remove_reference_t<T>>;
bool is_reference = cppmp::is_reference_v<T>;

if ( is_reference
&& !is_const
&& impl.is_const)
{
throw exception(
error_code::variant_invalid_cast,
"Can not get a reference to a const value");
}

return *reinterpret_cast<cppmp::remove_reference_t<T>*>(impl.data);
}
};

constexpr decltype(auto) extract_value = cppmp::generic_predicate<__impl::value_extractor> { };

}

/* variant */

variant::variant(__impl::variant_impl_ptr_s p_impl)
: _impl(p_impl)
{ }

variant::variant()
: _impl()
{ }

const type& variant::type() const
{
check_impl();
return _impl->type;
}

bool variant::is_reference() const
{
check_impl();
return _impl->is_reference;
}

bool variant::is_const() const
{
check_impl();
return _impl->is_const;
}

template<typename T>
T variant::get() const
{
check_impl();

if (_impl->type.id() != get_type_id<cppmp::decay_t<T>>())
{
throw exception(
error_code::variant_type_mismatch,
cppcore::string_builder()
<< "The vaiant does not store the requested type (requested="
<< get_type_name<cppmp::decay_t<T>>()
<< ", stored="
<< _impl->type.name()
<< ")");
}

return __impl::extract_value(*_impl, cppmp::type_v<T>);
}

bool variant::empty()
{ return !static_cast<bool>(_impl); }

variant::operator bool() const
{ return static_cast<bool>(_impl); }

void variant::check_impl() const
{
if (!_impl)
{
throw exception(
error_code::variant_is_empty,
"The variant does not store any value!");
}
}

}

+ 96
- 0
include/cpprtti/registry.h Ver ficheiro

@@ -0,0 +1,96 @@
#pragma once

#include <map>
#include <memory>

#include "types/type.h"

namespace cpprtti
{

struct registry
{
public:
using type_ptr_s = std::shared_ptr<type>;
using type_id_map = std::map<type_id, type_ptr_s>;
using type_name_map = std::map<std::string, type_ptr_s>;

private:
type_id_map _id_map;
type_name_map _name_map;

public:
/**
* @brief Get a type from the registry.
*
* This method will return the type for the requested parameter.
* It may create a new type object with the default implementation,
* if the type does not exist.
*/
template<typename T_type>
inline auto& get();

/**
* @brief Get a type from the registry.
*
* This method will return the type for the requested parameter.
* If the type was not found, an exception is thrown.
*/
template<typename T_type>
inline const auto& get() const;

/**
* @brief Get a type from the registry.
*
* This method will return the type for the requested parameter.
* It may create a new type object with the passed implementation,
* if the type does not exist.
* If the type implementation does not match the requested one,
* an exception is thrown.
*/
template<typename T_type, typename T_impl>
inline auto& get();

/**
* @brief Search a type by it's represented C++ type.
*/
template<typename T_type>
inline type * find();

/**
* @brief Search a type by it's represented C++ type.
*/
template<typename T_type>
inline const type * find() const;

/**
* @brief Search a type by it's ID.
*/
inline type * find(type_id id);

/**
* @brief Search a type by it's ID.
*/
inline const type * find(type_id id) const;

/**
* @brief Search a type by it's name.
*/
inline type * find(const std::string& name);

/**
* @brief Search a type by it's name.
*/
inline const type * find(const std::string& name) const;

private:
/**
* @brief Create a new type for the passed C++ type with the passed implementation type.
*/
template<typename T_impl>
inline T_impl& create();
};

}

#include "registry.inl"

+ 140
- 0
include/cpprtti/registry.inl Ver ficheiro

@@ -0,0 +1,140 @@
#pragma once

#include <cppcore/misc/string_builder.h>

#include "registry.h"
#include "misc/exception.h"
#include "traits/default_type.h"

namespace cpprtti
{

/* registry */

template<typename T_type>
auto& registry::get()
{ return get<T_type, default_type_t<T_type>>(); }

template<typename T_type>
const auto& registry::get() const
{
using impl_type = default_type_t<T_type>;

auto * t = find<T_type>();

if (!t)
{
throw exception(
error_code::registry_type_not_found,
cppcore::string_builder()
<< "The requested type could not be found: "
<< get_type_name<T_type>());
}

auto * ret = dynamic_cast<const impl_type *>(t);
if (!ret)
{
throw exception(
error_code::registry_type_does_not_match,
cppcore::string_builder()
<< "The type does not match the expected implementation: "
<< get_type_name<impl_type>());
}

return *ret;
}

template<typename T_type, typename T_impl>
auto& registry::get()
{
auto * t = find<T_type>();
if (!t)
t = &create<T_impl>();

auto * ret = dynamic_cast<T_impl *>(t);
if (!ret)
{
throw exception(
error_code::registry_type_does_not_match,
cppcore::string_builder()
<< "The type does not match the expected implementation: "
<< get_type_name<T_impl>());
}

return * ret;
}

template<typename T_type>
type * registry::find()
{ return find(get_type_id<cppmp::decay_t<T_type>>()); }

template<typename T_type>
const type * registry::find() const
{ return find(get_type_id<cppmp::decay_t<T_type>>()); }

type * registry::find(type_id id)
{
auto it = _id_map.find(id);
return it == _id_map.end()
? nullptr
: it->second.get();
}

const type * registry::find(type_id id) const
{
auto it = _id_map.find(id);
return it == _id_map.end()
? nullptr
: it->second.get();
}

type * registry::find(const std::string& name)
{
auto it = _name_map.find(name);
return it == _name_map.end()
? nullptr
: it->second.get();
}

const type * registry::find(const std::string& name) const
{
auto it = _name_map.find(name);
return it == _name_map.end()
? nullptr
: it->second.get();
}

template<typename T_impl>
T_impl& registry::create()
{
auto impl = std::make_shared<T_impl>(*this);

auto id_it = _id_map.find(impl->id());
if (id_it != _id_map.end())
{
throw exception(
error_code::registry_id_already_exsists,
cppcore::string_builder()
<< "A type with the id '"
<< impl->id()
<< "' is already registered!");
}

auto name_it = _name_map.find(impl->name());
if (name_it != _name_map.end())
{
throw exception(
error_code::registry_name_already_exsists,
cppcore::string_builder()
<< "A type with the name '"
<< impl->name()
<< "' is already registered!");
}

_id_map.emplace_hint(id_it, impl->id(), impl);
_name_map.emplace_hint(name_it, impl->name(), impl);

return *impl;
}

}

+ 5
- 0
include/cpprtti/traits.h Ver ficheiro

@@ -0,0 +1,5 @@
#pragma once

#include "traits/default_type.h"

#include "traits/default_type.inl"

+ 14
- 0
include/cpprtti/traits/default_type.h Ver ficheiro

@@ -0,0 +1,14 @@
#pragma once

namespace cpprtti
{

template<typename T, typename = void>
struct default_type_trait;

template<typename T>
using default_type_t = typename default_type_trait<cppmp::decay_t<T>>::type;

}

#include "default_type.inl"

+ 27
- 0
include/cpprtti/traits/default_type.inl Ver ficheiro

@@ -0,0 +1,27 @@
#pragma once

#include "default_type.h"

#include "../types/class_type_tpl.h"
#include "../types/fundamental_type_tpl.h"

namespace cpprtti
{

template<typename T>
struct default_type_trait<
T,
std::enable_if_t<std::is_fundamental_v<T>>>
{
using type = fundamental_type_tpl<T>;
};

template<typename T>
struct default_type_trait<
T,
std::enable_if_t<std::is_class_v<T>>>
{
using type = class_type_tpl<T>;
};

}

+ 14
- 0
include/cpprtti/types.h Ver ficheiro

@@ -0,0 +1,14 @@
#pragma once

#include "types/class_type_tpl.h"
#include "types/class_type.h"
#include "types/fundamental_type_tpl.h"
#include "types/fundamental_type.h"
#include "types/member.h"
#include "types/type.h"

#include "types/class_type_tpl.inl"
#include "types/class_type.inl"
#include "types/fundamental_type_tpl.inl"
#include "types/fundamental_type.inl"
#include "types/type.inl"

+ 55
- 0
include/cpprtti/types/class_type.h Ver ficheiro

@@ -0,0 +1,55 @@
#pragma once

#include <map>
#include <memory>

#include "type.h"
#include "member/member.h"

namespace cpprtti
{

struct class_type
: public type
{
private:
using member_ptr_u = std::unique_ptr<member>;
using member_map = std::map<std::string, member_ptr_u>;

private:
member_map _members;

public:
/**
* @brief Constructor.
*/
inline class_type(
cpprtti::registry& p_registry,
type_id p_id,
const std::string& p_name);

/**
* @brief Get a map of all registered members.
*/
const member_map& members() const;

/**
* @brief Register a new member variable.
*/
template<typename... T_args>
auto& add_member_variable(
const std::string& p_name,
T_args&&... p_args);

/**
* @brief Register a new member method.
*/
template<typename... T_args>
auto& add_member_method(
const std::string& p_name,
T_args&&... p_args);
};

}

#include "class_type.inl"

+ 71
- 0
include/cpprtti/types/class_type.inl Ver ficheiro

@@ -0,0 +1,71 @@
#pragma once

#include "class_type.h"
#include "member/member_method.h"
#include "member/member_variable.h"

namespace cpprtti
{

/* class_type */

class_type::class_type(
cpprtti::registry& p_registry,
type_id p_id,
const std::string& p_name)
: type(p_registry, p_id, p_name, rtti_type_t::class_type)
{ }

const class_type::member_map& class_type
::members() const
{ return _members; }

template<typename... T_args>
auto& class_type
::add_member_variable(
const std::string& p_name,
T_args&&... p_args)
{
auto it = _members.find(p_name);
if (it != _members.end())
{
throw exception(
error_code::class_member_already_exists,
cppcore::string_builder()
<< "Member with the name '"
<< p_name
<< "' already exists");
}

auto ptr = make_member_variable(*this, p_name, std::forward<T_args>(p_args)...);
auto& ret = *ptr;
_members.emplace_hint(it, p_name, std::move(ptr));

return ret;
}

template<typename... T_args>
auto& class_type
::add_member_method(
const std::string& p_name,
T_args&&... p_args)
{
auto it = _members.find(p_name);
if (it != _members.end())
{
throw exception(
error_code::class_member_already_exists,
cppcore::string_builder()
<< "Member with the name '"
<< p_name
<< "' already exists");
}

auto ptr = make_member_method(*this, p_name, std::forward<T_args>(p_args)...);
auto& ret = *ptr;
_members.emplace_hint(it, p_name, std::move(ptr));

return ret;
}

}

+ 25
- 0
include/cpprtti/types/class_type_tpl.h Ver ficheiro

@@ -0,0 +1,25 @@
#pragma once

#include "class_type.h"

namespace cpprtti
{

template<typename T_value>
struct class_type_tpl
: public class_type
{
public:
using value_type = T_value;

public:
/**
* @brief Constructor.
*/
inline class_type_tpl(
cpprtti::registry& p_registry);
};

}

#include "class_type_tpl.inl"

+ 20
- 0
include/cpprtti/types/class_type_tpl.inl Ver ficheiro

@@ -0,0 +1,20 @@
#pragma once

#include "class_type_tpl.h"
#include "../misc/helper.h"

namespace cpprtti
{

/* class_type_tpl */

template<typename T_value>
class_type_tpl<T_value>::class_type_tpl(
cpprtti::registry& p_registry)
: class_type(
p_registry,
get_type_id<value_type>(),
get_type_name<value_type>())
{ }

}

+ 23
- 0
include/cpprtti/types/fundamental_type.h Ver ficheiro

@@ -0,0 +1,23 @@
#pragma once

#include "type.h"

namespace cpprtti
{

struct fundamental_type
: public type
{
public:
/**
* @brief Constructor.
*/
inline fundamental_type(
cpprtti::registry& p_registry,
size_t p_id,
const std::string& p_name);
};

}

#include "fundamental_type.inl"

+ 17
- 0
include/cpprtti/types/fundamental_type.inl Ver ficheiro

@@ -0,0 +1,17 @@
#pragma once

#include "fundamental_type.h"

namespace cpprtti
{

/* fundamental_type */

fundamental_type::fundamental_type(
cpprtti::registry& p_registry,
size_t p_id,
const std::string& p_name)
: type(p_registry, p_id, p_name, rtti_type_t::fundamental_type)
{ }

}

+ 25
- 0
include/cpprtti/types/fundamental_type_tpl.h Ver ficheiro

@@ -0,0 +1,25 @@
#pragma once

#include "fundamental_type.h"

namespace cpprtti
{

template<typename T_value>
struct fundamental_type_tpl
: public fundamental_type
{
public:
using value_type = T_value;

public:
/**
* @brief Constructor.
*/
inline fundamental_type_tpl(
cpprtti::registry& p_registry);
};

}

#include "fundamental_type_tpl.inl"

+ 19
- 0
include/cpprtti/types/fundamental_type_tpl.inl Ver ficheiro

@@ -0,0 +1,19 @@
#pragma once

#include "fundamental_type_tpl.h"

namespace cpprtti
{

/* fundamental_type_tpl */

template<typename T_value>
fundamental_type_tpl<T_value>::fundamental_type_tpl(
cpprtti::registry& p_registry)
: fundamental_type(
p_registry,
get_type_id<value_type>(),
get_type_name<value_type>())
{ }

}

+ 9
- 0
include/cpprtti/types/member.h Ver ficheiro

@@ -0,0 +1,9 @@
#pragma once

#include "member/member_method.h"
#include "member/member_variable.h"
#include "member/member.h"

#include "member/member_method.inl"
#include "member/member_variable.inl"
#include "member/member.inl"

+ 56
- 0
include/cpprtti/types/member/member.h Ver ficheiro

@@ -0,0 +1,56 @@
#pragma once

#include <string>

#include "../../types/type.h"

namespace cpprtti
{

enum class member_type
{
unknown = 0,
variable,
method,
};

struct member
{
protected:
cpprtti::type& _owner;
std::string _name;
member_type _member_type;

public:
/**
* @brief Constructor.
*/
inline member(
cpprtti::type& p_owner,
const std::string& p_name,
member_type p_member_type);

/**
* @brief Destructor.
*/
virtual ~member() = default;

/**
* @brief Type that owns this member.
*/
inline const cpprtti::type& owner() const;

/**
* @brief Get the name of the member.
*/
inline const std::string& name() const;

/**
* @brief Type of the member.
*/
inline cpprtti::member_type member_type() const;
};

}

#include "member.inl"

+ 28
- 0
include/cpprtti/types/member/member.inl Ver ficheiro

@@ -0,0 +1,28 @@
#pragma once

#include "member.h"

namespace cpprtti
{

/* member */

member::member(
cpprtti::type& p_owner,
const std::string& p_name,
cpprtti::member_type p_member_type)
: _owner (p_owner)
, _name (p_name)
, _member_type (p_member_type)
{ }

const cpprtti::type& member::owner() const
{ return _owner; }

const std::string& member::name() const
{ return _name; }

member_type cpprtti::member::member_type() const
{ return _member_type; }

}

+ 46
- 0
include/cpprtti/types/member/member_method.h Ver ficheiro

@@ -0,0 +1,46 @@
#pragma once

#include <cppmp/misc/generic_predicate.h>

#include "member.h"

namespace cpprtti
{

namespace __impl
{

template<typename X, typename = void>
struct member_method_builder;

}

struct member_method
: public member
{
public:
/**
* @brief Constructor.
*/
inline member_method(
cpprtti::type& p_owner,
const std::string& p_name);

/**
* @brief Invoke the member method.
*/
template<typename T_object, typename... T_args>
inline variant invoke(T_object&& obj, T_args&&... args) const;

protected:
/**
* @brief Invoke the member method.
*/
virtual variant invoke_impl(const variant& obj, const std::vector<variant>& args) const = 0;
};

constexpr decltype(auto) make_member_method = cppmp::generic_predicate<__impl::member_method_builder> { };

}

#include "member_method.inl"

+ 113
- 0
include/cpprtti/types/member/member_method.inl Ver ficheiro

@@ -0,0 +1,113 @@
#pragma once

#include <cppmp/traits/lambda_traits.h>

#include "member_method.h"

namespace cpprtti
{

/* member_method */

member_method::member_method(
cpprtti::type& p_owner,
const std::string& p_name)
: member(
p_owner,
p_name,
cpprtti::member_type::method)
{ }

template<typename T_object, typename... T_args>
variant member_method
::invoke(T_object&& obj, T_args&&... args) const
{
return invoke_impl(
make_variant(_owner.registry(), std::forward<T_object>(obj)),
std::vector<variant>({
make_variant(_owner.registry(), std::forward<T_args>(args))...
}));
}

namespace __impl
{

/* member_method_builder - default */

template<typename X, typename>
struct member_method_builder
{
using is_default = cppmp::true_t;

template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&...)
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_member_method(...)!"); }
};

/* member_method_builder - member method */

template<typename T_method>
struct member_method_builder<
cppmp::list<class_type&, const std::string&, T_method>,
cppmp::enable_if_t<
cppmp::is_valid_v<cppmp::lambda_traits<T_method>>
&& !cppmp::lambda_traits<T_method>::is_static_v
>
>
{
using method_type = T_method;
using lambda_trait = cppmp::lambda_traits<method_type>;
using object_type = typename lambda_trait::object_type;

struct member_impl
: public member_method
{
private:
method_type _member;

public:
inline member_impl(
cpprtti::type& p_owner,
const std::string& p_name,
method_type p_member)
: member_method(p_owner, p_name)
, _member(p_member)
{
if (_owner.id() != get_type_id<cppmp::decay_t<object_type>>())
{
throw exception(
error_code::class_type_mismatch,
"Object type of the member method does not match the type of the owner!");
}
}

protected:
variant invoke_impl(const variant& obj, const std::vector<variant>& args) const override
{ return invoke_helper(obj, args, std::make_index_sequence<lambda_trait::argument_count_v> { }); }

private:
template<size_t... I>
inline variant invoke_helper(const variant& obj, const std::vector<variant>& args, std::index_sequence<I...>) const
{
return make_variant(
_owner.registry(),
(obj.get<object_type&>().*_member)(
args[I].get<typename lambda_trait::template argument_t<I>>()...
)
);
}
};

template<typename X_member>
static constexpr decltype(auto) apply(type& owner, const std::string& name, X_member&& member)
{
return std::make_unique<member_impl>(
owner,
name,
std::forward<X_member>(member));
}
};

}

}

+ 79
- 0
include/cpprtti/types/member/member_variable.h Ver ficheiro

@@ -0,0 +1,79 @@
#pragma once

#include <cppmp/misc/generic_predicate.h>

#include "member.h"
#include "../../misc/variant.h"

namespace cpprtti
{

namespace __impl
{

template<typename X, typename = void>
struct member_variable_builder;

}

struct member_variable
: public member
{
protected:
cpprtti::type& _type;
bool _is_readale { true };
bool _is_writable { true };

public:
/**
* @brief Constructor.
*/
inline member_variable(
cpprtti::type& p_owner,
const std::string& p_name,
cpprtti::type& p_type);

/**
* @brief RTTI type of that member represents.
*/
const cpprtti::type& type() const;

/**
* @brief Check if the variable is readable.
*/
bool is_readale() const;

/**
* @brief Check if the variable is writable.
*/
bool is_writable() const;

/**
* @brief Get the current value of the member.
*/
template<typename T_object>
inline variant get(T_object& obj) const;

/**
* @brief Set the new value of the member.
*/
template<typename T_object, typename T_value>
inline void set(T_object& obj, const T_value& value) const;

protected:
/**
* @brief Get the current value of the member.
*/
virtual variant get_impl(const variant& obj) const = 0;

/**
* @brief Set the new value of the member.
*/
virtual void set_impl(const variant& obj, const variant& value) const = 0;
};

constexpr decltype(auto) make_member_variable = cppmp::generic_predicate<__impl::member_variable_builder> { };

}

#include "member_variable.inl"

+ 387
- 0
include/cpprtti/types/member/member_variable.inl Ver ficheiro

@@ -0,0 +1,387 @@
#pragma once

#include <cppmp/misc/getter.h>
#include <cppmp/misc/setter.h>

#include "member_variable.h"

namespace cpprtti
{

/* member_variable */

member_variable::member_variable(
cpprtti::type& p_owner,
const std::string& p_name,
cpprtti::type& p_type)
: member(
p_owner,
p_name,
member_type::variable)
, _type(p_type)
{ }

const cpprtti::type& member_variable::type() const
{ return _type; }

bool member_variable::is_readale() const
{ return _is_readale; }

bool member_variable::is_writable() const
{ return _is_writable; }

template<typename T_object>
variant member_variable::get(T_object& obj) const
{
if (!_is_readale)
{
throw exception(
error_code::class_member_not_readable,
cppcore::string_builder()
<< "Member variable '"
<< _name
<< "' of '"
<< _owner.name()
<< " is not readable!");
}
return get_impl(make_variant(_owner.registry(), obj));
}

template<typename T_object, typename T_value>
inline void member_variable::set(T_object& obj, const T_value& value) const
{
if (!_is_writable)
{
throw exception(
error_code::class_member_not_writable,
cppcore::string_builder()
<< "Member variable '"
<< _name
<< "' of '"
<< _owner.name()
<< " is not writable!");
}
return set_impl(make_variant(_owner.registry(), obj), make_variant(_owner.registry(), value));
}

namespace __impl
{

/* getter_info_trait */

template<typename T_getter, typename = void>
struct getter_info_trait;

template<typename T_getter>
struct getter_info_trait<
T_getter,
cppmp::enable_if_t<
decltype(cppmp::make_getter)::is_valid_v<T_getter>
>
>
{
using getter_type = decltype(cppmp::make_getter(std::declval<T_getter>()));
using object_type = typename getter_type::object_type;
using value_type = typename getter_type::value_type;

static constexpr decltype(auto) is_read_only_v =
cppmp::is_const_v<cppmp::remove_reference_t<object_type>>
|| cppmp::is_const_v<cppmp::remove_reference_t<value_type>>;
};

/* setter_info_trait */

template<typename T_setter, typename = void>
struct setter_info_trait;

template<typename T_setter>
struct setter_info_trait<
T_setter,
cppmp::enable_if_t<
decltype(cppmp::make_setter)::is_valid_v<T_setter>
>
>
{
using setter_type = decltype(cppmp::make_setter(std::declval<T_setter>()));
using object_type = typename setter_type::object_type;
using value_type = typename setter_type::value_type;

static constexpr decltype(auto) is_write_only_v =
!cppmp::is_base_of_v<cppmp::tag_setter_member_var, setter_type>;
};

/* member_variable_builder - default */

template<typename X, typename>
struct member_variable_builder
{
using is_default = cppmp::true_t;

template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&...)
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_member_variable(...)!"); }
};

/* member_variable_builder - read-write getter */

template<typename T_getter>
struct member_variable_builder<
cppmp::list<class_type&, const std::string&, T_getter>,
cppmp::enable_if_t<
decltype(cppmp::make_getter)::is_valid_v<T_getter>
&& !getter_info_trait<T_getter>::is_read_only_v
>
>
{
using getter_type = cppmp::decay_t<decltype(cppmp::make_getter(std::declval<T_getter>()))>;
using object_type = typename getter_type::object_type;
using value_type = typename getter_type::value_type;

struct member_impl
: public member_variable
{
private:
getter_type _getter;

public:
inline member_impl(
cpprtti::type& p_owner,
const std::string& p_name,
getter_type p_getter)
: member_variable(p_owner, p_name, p_owner.registry().get<value_type>())
, _getter(p_getter)
{
if (_owner.id() != get_type_id<cppmp::decay_t<object_type>>())
{
throw exception(
error_code::class_type_mismatch,
"Object type of the getter does not match the type of the owner!");
}

_is_readale = true;
_is_writable = true;
}

protected:
variant get_impl(const variant& obj) const override
{
return obj.is_const()
? make_variant(_owner.registry(), _getter(obj.get<const object_type&>()))
: make_variant(_owner.registry(), _getter(obj.get<object_type&>()));
}

void set_impl(const variant& obj, const variant& value) const override
{ _getter(obj.get<object_type&>()) = value.get<value_type&>(); }
};

template<typename X_getter>
static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter)
{
return std::make_unique<member_impl>(
owner,
name,
cppmp::make_getter(std::forward<X_getter>(getter)));
}
};

/* member_variable_builder - read-only getter */

template<typename T_getter>
struct member_variable_builder<
cppmp::list<class_type&, const std::string&, T_getter>,
cppmp::enable_if_t<
decltype(cppmp::make_getter)::is_valid_v<T_getter>
&& getter_info_trait<T_getter>::is_read_only_v
>
>
{
using getter_type = cppmp::decay_t<decltype(cppmp::make_getter(std::declval<T_getter>()))>;
using object_type = typename getter_type::object_type;
using value_type = typename getter_type::value_type;

struct member_impl
: public member_variable
{
private:
getter_type _getter;

public:
inline member_impl(
cpprtti::type& p_owner,
const std::string& p_name,
getter_type p_getter)
: member_variable(p_owner, p_name, p_owner.registry().get<value_type>())
, _getter(p_getter)
{
if (_owner.id() != get_type_id<cppmp::decay_t<object_type>>())
{
throw exception(
error_code::class_type_mismatch,
"Object type of the getter does not match the type of the owner!");
}

_is_readale = true;
_is_writable = false;
}

protected:
variant get_impl(const variant& obj) const override
{ return make_variant(_owner.registry(), _getter(obj.get<const object_type&>())); }

void set_impl(const variant& obj, const variant& value) const override
{ }
};

template<typename X_getter>
static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter)
{
return std::make_unique<member_impl>(
owner,
name,
cppmp::make_getter(std::forward<X_getter>(getter)));
}
};

/* member_variable_builder - write-only setter */

template<typename T_setter>
struct member_variable_builder<
cppmp::list<class_type&, const std::string&, T_setter>,
cppmp::enable_if_t<
decltype(cppmp::make_setter)::is_valid_v<T_setter>
&& setter_info_trait<T_setter>::is_write_only_v
>
>
{
using setter_type = cppmp::decay_t<decltype(cppmp::make_setter(std::declval<T_setter>()))>;
using object_type = typename setter_type::object_type;
using value_type = typename setter_type::value_type;

struct member_impl
: public member_variable
{
private:
setter_type _setter;

public:
inline member_impl(
cpprtti::type& p_owner,
const std::string& p_name,
setter_type p_setter)
: member_variable(p_owner, p_name, p_owner.registry().get<value_type>())
, _setter(p_setter)
{
if (_owner.id() != get_type_id<cppmp::decay_t<object_type>>())
{
throw exception(
error_code::class_type_mismatch,
"Object type of the setter does not match the type of the owner!");
}

_is_readale = false;
_is_writable = true;
}

protected:
variant get_impl(const variant& obj) const override
{ return variant(); }

void set_impl(const variant& obj, const variant& value) const override
{ _setter(obj.get<object_type&>(), value.get<const value_type&>()); }
};

template<typename X_setter>
static constexpr decltype(auto) apply(type& owner, const std::string& name, X_setter&& setter)
{
return std::make_unique<member_impl>(
owner,
name,
cppmp::make_setter(std::forward<X_setter>(setter)));
}
};

/* member_variable_builder - read-write getter/setter */

template<typename T_getter, typename T_setter>
struct member_variable_builder<
cppmp::list<class_type&, const std::string&, T_getter, T_setter>,
cppmp::enable_if_t<
decltype(cppmp::make_getter)::is_valid_v<T_getter>
&& decltype(cppmp::make_setter)::is_valid_v<T_setter>
>
>
{
using getter_type = cppmp::decay_t<decltype(cppmp::make_getter(std::declval<T_getter>()))>;
using getter_object_type = typename getter_type::object_type;
using getter_value_type = typename getter_type::value_type;

using setter_type = cppmp::decay_t<decltype(cppmp::make_setter(std::declval<T_setter>()))>;
using setter_object_type = typename setter_type::object_type;
using setter_value_type = typename setter_type::value_type;

using object_type = cppmp::decay_t<getter_object_type>;
using value_type = cppmp::decay_t<getter_value_type>;

static_assert(cppmp::is_same_v<
cppmp::decay_t<getter_object_type>,
cppmp::decay_t<setter_object_type>>,
"object type does not match");
static_assert(cppmp::is_same_v<
cppmp::decay_t<getter_value_type>,
cppmp::decay_t<setter_value_type>>,
"value type does not match");

struct member_impl
: public member_variable
{
private:
getter_type _getter;
setter_type _setter;

public:
inline member_impl(
cpprtti::type& p_owner,
const std::string& p_name,
getter_type p_getter,
setter_type p_setter)
: member_variable(p_owner, p_name, p_owner.registry().get<value_type>())
, _getter(p_getter)
, _setter(p_setter)
{
if (_owner.id() != get_type_id<cppmp::decay_t<object_type>>())
{
throw exception(
error_code::class_type_mismatch,
"Object type of the setter does not match the type of the owner!");
}

_is_readale = true;
_is_writable = true;
}

protected:
variant get_impl(const variant& obj) const override
{
return obj.is_const()
? make_variant(_owner.registry(), _getter(obj.get<const getter_object_type&>()))
: make_variant(_owner.registry(), _getter(obj.get<getter_object_type&>()));
}

void set_impl(const variant& obj, const variant& value) const override
{ _setter(obj.get<setter_object_type&>(), value.get<setter_value_type>()); }
};

template<typename X_getter, typename X_setter>
static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter, X_setter&& setter)
{
return std::make_unique<member_impl>(
owner,
name,
cppmp::make_getter(std::forward<X_getter>(getter)),
cppmp::make_setter(std::forward<X_setter>(setter)));
}
};

}

}

+ 75
- 0
include/cpprtti/types/type.h Ver ficheiro

@@ -0,0 +1,75 @@
#pragma once

#include <string>

#include <cpprtti/misc/helper.h>

namespace cpprtti
{

struct registry;

enum class rtti_type
{
unknown = 0, //!< unknown
fundamental_type, //!< fundamental C++ type (like int, float, ...)
class_type, //!< C++ class/struct
};

using rtti_type_t = enum rtti_type;

/**
* @brief represents any C++ type
*/
struct type
{
protected:
cpprtti::registry& _registry; //!< Type registry this type belongs to.
type_id _id; //!< Unique ID
std::string _name; //!< Name of the type.
rtti_type_t _rtti_type; //!< Type specialization

public:
/**
* @brief Constructor.
*/
inline type(
cpprtti::registry& p_registry,
size_t p_id,
const std::string& p_name,
rtti_type_t p_rtti_type);

/**
* @brief Destructor.
*/
virtual ~type() = default;

/**
* @brief Type registry this type belongs to.
*/
inline cpprtti::registry& registry();

/**
* @brief Type registry this type belongs to.
*/
inline const cpprtti::registry& registry() const;

/**
* @brief Get the ID of this type.
*/
inline type_id id() const;

/**
* @brief Get the name of this type.
*/
inline const std::string& name() const;

/**
* @brief Get the RTTI type of this type object.
*/
inline rtti_type_t rtti_type() const;
};

}

#include "type.inl"

+ 36
- 0
include/cpprtti/types/type.inl Ver ficheiro

@@ -0,0 +1,36 @@
#pragma once

#include "type.h"

namespace cpprtti
{

/* type */

type::type(
cpprtti::registry& p_registry,
size_t p_id,
const std::string& p_name,
rtti_type_t p_rtti_type)
: _registry (p_registry)
, _id (p_id)
, _name (p_name)
, _rtti_type(p_rtti_type)
{ }

cpprtti::registry& type::registry()
{ return _registry; }

const cpprtti::registry& type::registry() const
{ return _registry; }

type_id type::id() const
{ return _id; }

const std::string& type::name() const
{ return _name; }

rtti_type_t type::rtti_type() const
{ return _rtti_type; }

}

+ 38
- 0
src/CMakeLists.txt Ver ficheiro

@@ -0,0 +1,38 @@
# Initialize ######################################################################################

Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE )
Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC )
Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS )

Find_Package ( cppcore )
Find_Package ( cppmp )

# Object Library ##################################################################################

Set ( CPPRTTI_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include )
Add_Library ( cpprtti
INTERFACE )
Target_Include_Directories ( cpprtti
INTERFACE
$<BUILD_INTERFACE:${CPPRTTI_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CPPRTTI_INSTALL_DIR_INCLUDE}> )
Target_Link_Libraries ( cpprtti
INTERFACE
cppcore
cppmp )

# Install #########################################################################################

Set ( CPPRTTI_HAS_EXPORT False PARENT_SCOPE )

# Header
If ( CPPRTTI_INSTALL_HEADER )
Set ( CPPRTTI_HAS_EXPORT True PARENT_SCOPE )
Install ( FILES ${CPPRTTI_INCLUDE_DIR}/cpprtti.h
DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} )
Install ( DIRECTORY ${CPPRTTI_INCLUDE_DIR}/cpprtti
DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} )
Install ( TARGETS cpprtti
EXPORT cpprtti
DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} )
EndIf ( )

+ 52
- 0
test/CMakeLists.txt Ver ficheiro

@@ -0,0 +1,52 @@
# Initialize ######################################################################################

Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE )
Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC )
Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS )

# Test ############################################################################################

Find_Package ( GTest )
If ( NOT "${GTest_FOUND}" )
Return ( )
EndIf ( )

File ( GLOB_RECURSE CPPRTTI_TEST_HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/*.h )
File ( GLOB_RECURSE CPPRTTI_TEST_INLINE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/*.inl )
File ( GLOB_RECURSE CPPRTTI_TEST_SOURCE_FILES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )

ForEach ( FILE IN LISTS CPPRTTI_TEST_SOURCE_FILES )
# add test
Get_Filename_Component ( TEST_DIR ${FILE} DIRECTORY )
Get_Filename_Component ( TEST_NAME ${FILE} NAME_WE )
Set ( TEST_NAME "${TEST_DIR}/${TEST_NAME}" )
String ( REPLACE "\\" "-" TEST_NAME "${TEST_NAME}" )
String ( REPLACE "/" "-" TEST_NAME "${TEST_NAME}" )
String ( REPLACE "_" "-" TEST_NAME "${TEST_NAME}" )
Set ( TEST_NAME "test-${TEST_NAME}" )
Add_Executable ( ${TEST_NAME}
EXCLUDE_FROM_ALL
${CPPRTTI_TEST_HEADER_FILES}
${CPPRTTI_TEST_INLINE_FILES}
${FILE} )
Target_Link_Libraries ( ${TEST_NAME}
PUBLIC
cpprtti
GTest::Main )

# pedantic
If ( HAS_PEDANTIC )
Pedantic_Apply_Flags_Target ( ${TEST_NAME} ALL )
EndIf ( )

# test
If ( HAS_CMAKE_TESTS )
Add_CMake_Test ( NAME ${TEST_NAME} TARGET ${TEST_NAME} GROUP cpprtti )
Else ( )
Add_Test ( NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
EndIf ( )
EndForEach ( )

+ 101
- 0
test/cpprtti/misc/variant_tests.cpp Ver ficheiro

@@ -0,0 +1,101 @@
#include <gtest/gtest.h>
#include <cpprtti/misc/variant.h>

using namespace cpprtti;

TEST(variant_test, empty)
{
variant v;
EXPECT_TRUE (v.empty());
EXPECT_FALSE(static_cast<bool>(v));
}

TEST(variant_test, store_normal_value)
{
registry reg;
int i = 6;
auto v = make_variant(reg, static_cast<int>(i));

EXPECT_FALSE (v.is_reference());
EXPECT_FALSE (v.is_const());

EXPECT_EQ (v.get<int>(), 6);
EXPECT_EQ (v.get<int&>(), 6);
EXPECT_EQ (v.get<int&&>(), 6);

EXPECT_EQ (v.get<const int>(), 6);
EXPECT_EQ (v.get<const int&>(), 6);
EXPECT_EQ (v.get<const int&&>(), 6);
}

TEST(variant_test, store_constant_value)
{
registry reg;
int i = 6;
auto v = make_variant(reg, static_cast<int>(i));

EXPECT_FALSE (v.is_reference());
EXPECT_FALSE (v.is_const());

EXPECT_EQ (v.get<int>(), 6);
EXPECT_EQ (v.get<int&>(), 6);
EXPECT_EQ (v.get<int&&>(), 6);

EXPECT_EQ (v.get<const int>(), 6);
EXPECT_EQ (v.get<const int&>(), 6);
EXPECT_EQ (v.get<const int&&>(), 6);
}

TEST(variant_test, store_lvalue_reference)
{
registry reg;
int i = 6;
auto v = make_variant(reg, static_cast<int&>(i));

EXPECT_TRUE (v.is_reference());
EXPECT_FALSE (v.is_const());

EXPECT_EQ (v.get<int>(), 6);
EXPECT_EQ (v.get<int&>(), 6);
EXPECT_EQ (v.get<int&&>(), 6);

EXPECT_EQ (v.get<const int>(), 6);
EXPECT_EQ (v.get<const int&>(), 6);
EXPECT_EQ (v.get<const int&&>(), 6);
}

TEST(variant_test, store_constant_lvalue_reference)
{
registry reg;
int i = 6;
auto v = make_variant(reg, static_cast<const int&>(i));

EXPECT_TRUE (v.is_reference());
EXPECT_TRUE (v.is_const());

EXPECT_EQ (v.get<int>(), 6);
EXPECT_ANY_THROW(v.get<int&>());
EXPECT_ANY_THROW(v.get<int&&>());

EXPECT_EQ (v.get<const int>(), 6);
EXPECT_EQ (v.get<const int&>(), 6);
EXPECT_ANY_THROW(v.get<const int&&>());
}

TEST(variant_test, store_rvalue_reference)
{
registry reg;
int i = 6;
auto v = make_variant(reg, std::move(i));

EXPECT_FALSE (v.is_reference());
EXPECT_FALSE (v.is_const());

EXPECT_EQ (v.get<int>(), 6);
EXPECT_EQ (v.get<int&>(), 6);
EXPECT_EQ (v.get<int&&>(), 6);

EXPECT_EQ (v.get<const int>(), 6);
EXPECT_EQ (v.get<const int&>(), 6);
EXPECT_EQ (v.get<const int&&>(), 6);
}

+ 111
- 0
test/cpprtti/type/class_type_tests.cpp Ver ficheiro

@@ -0,0 +1,111 @@
#include <gtest/gtest.h>
#include <cpprtti.h>

using namespace cpprtti;

struct test_class
{
public:
int i;
std::string read_only;
std::string write_only;
std::string read_write;

public:
inline const std::string& get_read_only() const
{ return read_only; }

inline void set_write_only(const std::string& value)
{ write_only = value; }

inline const std::string& get_read_write() const
{ return read_write; }

inline void set_read_write(const std::string& value)
{ read_write = value; }

inline int add(int value)
{ return i += value; }
};

TEST(class_type_tests, member_variable_read_write_getter)
{
registry reg;
auto& c = reg.get<test_class>();

test_class obj;
obj.i = 10;

auto& m = c.add_member_variable("i", &test_class::i);
auto v = m.get(obj);
EXPECT_TRUE (m.is_readale());
EXPECT_TRUE (m.is_writable());
EXPECT_TRUE (v.is_reference());
EXPECT_FALSE(v.is_const());
EXPECT_EQ (v.get<int>(), 10);
}

TEST(class_type_tests, member_variable_read_only_getter)
{
registry reg;
auto& c = reg.get<test_class>();

test_class obj;
obj.read_only = "read_only";

auto& m = c.add_member_variable("read_only", &test_class::get_read_only);
auto v = m.get(obj);
EXPECT_TRUE (m.is_readale());
EXPECT_FALSE(m.is_writable());
EXPECT_TRUE (v.is_reference());
EXPECT_TRUE (v.is_const());
EXPECT_EQ (v.get<std::string>(), "read_only");
}

TEST(class_type_tests, member_variable_write_only_setter)
{
registry reg;
auto& c = reg.get<test_class>();

test_class obj;

auto& m = c.add_member_variable("write_only", &test_class::set_write_only);
EXPECT_FALSE(m.is_readale());
EXPECT_TRUE (m.is_writable());
m.set(obj, std::string("fuuu"));
EXPECT_EQ(obj.write_only, "fuuu");
}

TEST(class_type_tests, member_variable_read_write_getter_setter)
{
registry reg;
auto& c = reg.get<test_class>();

test_class obj;
obj.read_write = "read_write";

auto& m = c.add_member_variable("read_write", &test_class::get_read_write, &test_class::set_read_write);
auto v = m.get(obj);
EXPECT_TRUE (m.is_readale());
EXPECT_TRUE (m.is_writable());
EXPECT_TRUE (v.is_reference());
EXPECT_TRUE (v.is_const());
EXPECT_EQ (v.get<std::string>(), "read_write");
m.set(obj, std::string("fuuu"));
EXPECT_EQ(obj.read_write, "fuuu");
}

TEST(class_type_tests, member_method)
{
registry reg;
auto& c = reg.get<test_class>();

test_class obj;
obj.i = 10;

auto& m = c.add_member_method("add", &test_class::add);
auto v = m.invoke(obj, 5);

EXPECT_EQ(v.get<int>(), 15);
EXPECT_EQ(obj.i, 15);
}

Carregando…
Cancelar
Guardar