-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TODO] Implement the first version of tagged tuple
- Loading branch information
1 parent
554aad0
commit a555a30
Showing
4 changed files
with
336 additions
and
0 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
270 changes: 270 additions & 0 deletions
270
src/System/include/BipedalLocomotion/System/NamedTuple.h
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,270 @@ | ||
/** | ||
* @file NamedTuple.h | ||
* @authors Giulio Romualdi | ||
* @copyright 2023 Istituto Italiano di Tecnologia (IIT). This software may be modified and | ||
* distributed under the terms of the BSD-3-Clause license. | ||
*/ | ||
|
||
// The original idea of named_tuple and part of the code has been taken from | ||
// https://vitiy.info/named-tuple-for-cplusplus/ | ||
|
||
#ifndef NAMED_TUPLE_NAMED_TUPLE_H | ||
#define NAMED_TUPLE_NAMED_TUPLE_H | ||
|
||
#include <cstdint> | ||
#include <tuple> | ||
#include <type_traits> | ||
#include <utility> | ||
|
||
namespace BipedalLocomotion | ||
{ | ||
namespace System | ||
{ | ||
using hash_type = std::uint64_t; | ||
|
||
namespace detail | ||
{ | ||
|
||
// The following code has been taken from https://gist.github.com/Manu343726/081512c43814d098fe4b | ||
// it allows to generate a compile time string hash | ||
constexpr hash_type fnv_basis = 14695981039346656037ull; | ||
constexpr hash_type fnv_prime = 109951162821ull; | ||
|
||
// FNV-1a 64 bit hash | ||
constexpr hash_type sid_hash(const char* str, hash_type hash = fnv_basis) noexcept | ||
{ | ||
return *str ? sid_hash(str + 1, (hash ^ *str) * fnv_prime) : hash; | ||
} | ||
|
||
} // namespace detail | ||
|
||
/** | ||
* named_param is a struct that associate a compile time hash to a value. | ||
* @note Type Ts must be default constructible. | ||
*/ | ||
template <hash_type hash_, typename Type_> struct named_param | ||
{ | ||
static constexpr hash_type hash = hash_; | ||
using Type = Type_; | ||
|
||
Type param; /**< Parameter associated to the given hash */ | ||
}; | ||
|
||
/** | ||
* named_tuple is an class that inherits from tuple. It main purpose is to make more readable and | ||
* less error prone code that uses `std::tuple`. | ||
* \code{.cpp} | ||
* using namespace BipedalLocomotion::System::literals; | ||
* | ||
* auto foo = BipedalLocomotion::System::make_named_tuple(BipedalLocomotion::System::named_param<"name"_h, std::string>(), | ||
* BipedalLocomotion::System::named_param<"number"_h, int>()); | ||
* | ||
* // it is still possible to access the elements of the named_tuple with Structured binding | ||
* // declaration | ||
* auto& [a, b] = foo; | ||
* b = 150; | ||
* | ||
* // moreover you can call std::get<> as usual | ||
* int n = std::get<1>(foo); | ||
* | ||
* // Differently from a normal tuple it is possible to access to the element via the hash as | ||
* // follows | ||
* foo.get_from_hash<"name"_h>() = "Smith"; | ||
* const std::string& temp_string = foo.get_from_hash<"name"_h>(); | ||
* \endcode | ||
*/ | ||
template <typename... Params> struct named_tuple : public std::tuple<Params...> | ||
{ | ||
static constexpr std::size_t error = -1; | ||
|
||
/** | ||
* Constructor | ||
*/ | ||
constexpr named_tuple() | ||
: std::tuple<Params...>() | ||
{ | ||
} | ||
|
||
/** | ||
* Constructor | ||
*/ | ||
template <typename... Args> | ||
named_tuple(Args&&... args) | ||
: std::tuple<Args...>(std::forward<Args>(args)...) | ||
{ | ||
} | ||
|
||
/** | ||
* Get the index of the element given the hash. | ||
* @return an error flag. | ||
* @note In this function is returned means that the hash has not be found. | ||
*/ | ||
template <std::size_t I = 0, hash_type Hash> | ||
constexpr typename std::enable_if<I == sizeof...(Params), | ||
const std::size_t>::type static get_element_index() noexcept | ||
{ | ||
return error; | ||
} | ||
|
||
/** | ||
* Get the index of the element given the hash. | ||
* @return The index of the element associated to the given hash. | ||
* @note This function is resolved at compile time. So if the hash is not found an error flag | ||
* will be returned. | ||
*/ | ||
template <std::size_t I = 0, hash_type Hash> | ||
constexpr typename std::enable_if | ||
< I<sizeof...(Params), const std::size_t>::type static get_element_index() noexcept | ||
{ | ||
using elementType = typename std::tuple_element<I, std::tuple<Params...>>::type; | ||
if constexpr (Hash == elementType::hash) | ||
{ | ||
return I; | ||
} | ||
return get_element_index<I + 1, Hash>(); | ||
} | ||
|
||
/** | ||
* Extracts the Ith element from the named_tuple. I must be an integer value in `[0, | ||
* sizeof...(Types))`. | ||
* @param t the named_tuple | ||
* @return A reference to the selected element of t. | ||
*/ | ||
template <std::size_t I> | ||
const auto& get() const noexcept | ||
{ | ||
auto& param = (std::get<I>(static_cast<const std::tuple<Params...>&>(*this))); | ||
return param.param; | ||
} | ||
|
||
/** | ||
* Extracts the Ith element from the named_tuple. I must be an integer value in `[0, | ||
* sizeof...(Types))`. | ||
* @param t the named_tuple | ||
* @return A reference to the selected element of t. | ||
*/ | ||
template <std::size_t I> | ||
auto& get() noexcept | ||
{ | ||
auto& param = (std::get<I>(static_cast<std::tuple<Params...>&>(*this))); | ||
return param.param; | ||
} | ||
|
||
/** | ||
* Extracts the element from the named_tuple associated to the hash `Hash`. `Hash` must be | ||
* among one of the hash values associated to the tuple when created. | ||
* @@return A reference to the selected element of t. | ||
*/ | ||
template <hash_type Hash> const auto& get_from_hash() const noexcept | ||
{ | ||
constexpr std::size_t index = get_element_index<0, Hash>(); | ||
static_assert((index != error), "Wrong named tuple key."); | ||
return this->get<index>(); | ||
} | ||
|
||
/** | ||
* Extracts the element from the named_tuple associated to the hash `Hash`. `Hash` must be | ||
* among one of the hash values associated to the tuple when created. | ||
* @@return A reference to the selected element of t. | ||
*/ | ||
template <hash_type Hash> auto& get_from_hash() noexcept | ||
{ | ||
constexpr std::size_t index = get_element_index<0, Hash>(); | ||
static_assert((index != error), "Wrong named tuple key."); | ||
return this->get<index>(); | ||
} | ||
}; | ||
|
||
/** | ||
* Creates a named_tuple object, deducing the target type from the types of arguments. | ||
* @params args zero or more arguments to construct the named_tuple from | ||
* @return A named_tuple object containing the given values, created as if by | ||
* `named_tuple<Args...>(std::forward<Args>(args)...).` | ||
*/ | ||
template <typename... Args> constexpr auto make_named_tuple(Args&&... args) | ||
{ | ||
return named_tuple<Args...>(std::forward<Args>(args)...); | ||
} | ||
|
||
namespace literals | ||
{ | ||
|
||
/** | ||
* Forms an hash from a char* | ||
* @note To use the literal operator please refer to the following code | ||
* \code{.cpp} | ||
* using namespace BipedalLocomotion::System::literals; | ||
* | ||
* constexpr BipedalLocomotion::System::hash_type hash = "foo"_h; | ||
* \endcode | ||
*/ | ||
constexpr hash_type operator"" _h(const char* tag, std::size_t s) | ||
{ | ||
return ::BipedalLocomotion::System::detail::sid_hash(tag); | ||
} | ||
|
||
} // namespace literals | ||
|
||
} // namespace System | ||
} // namespace BipedalLocomotion | ||
namespace std | ||
{ | ||
|
||
/** | ||
* Extracts the Ith element from the named_tuple. I must be an integer value in `[0, | ||
* sizeof...(Types))`. | ||
* @param t the named_tuple | ||
* @return A reference to the selected element of t. | ||
*/ | ||
template <std::size_t index, typename... Params> | ||
const auto& get(const ::BipedalLocomotion::System::named_tuple<Params...>& t) noexcept | ||
{ | ||
return t.template get<index>(); | ||
} | ||
|
||
/** | ||
* Extracts the Ith element from the named_tuple. I must be an integer value in `[0, | ||
* sizeof...(Types))`. | ||
* @param t the named_tuple | ||
* @return A reference to the selected element of t. | ||
*/ | ||
template <std::size_t I, typename... Params> | ||
auto& get(::BipedalLocomotion::System::named_tuple<Params...>& t) noexcept | ||
{ | ||
return t.template get<I>(); | ||
} | ||
|
||
/** | ||
* Extracts the Ith element from the named_tuple. I must be an integer value in `[0, | ||
* sizeof...(Types))`. | ||
* @param t the named_tuple | ||
* @return A reference to the selected element of t. | ||
*/ | ||
template <std::size_t I, typename... Params> | ||
const std::tuple_element<I, std::tuple<typename Params::Type...>>& | ||
get(const ::BipedalLocomotion::System::named_tuple<Params...>& t) noexcept | ||
{ | ||
return t.template get<I>(); | ||
} | ||
/** | ||
* Template specialization to make the elements of the named_tuple accessible with the Structured | ||
* binding declaration | ||
*/ | ||
template <typename... Params> | ||
struct tuple_size<::BipedalLocomotion::System::named_tuple<Params...>> : integral_constant<size_t, sizeof...(Params)> | ||
{ | ||
}; | ||
|
||
/** | ||
* Template specialization to make the elements of the named_tuple accessible with the Structured | ||
* binding declaration | ||
*/ | ||
template <size_t Index, typename... Params> | ||
struct tuple_element<Index, ::BipedalLocomotion::System::named_tuple<Params...>> | ||
: std::tuple_element<Index, std::tuple<typename Params::Type...>> | ||
{ | ||
}; | ||
|
||
} // namespace std | ||
|
||
#endif // NAMED_TUPLE_NAMED_TUPLE_H |
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
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,60 @@ | ||
/** | ||
* @file TaggedTupleTest.cpp | ||
* @authors Giulio Romualdi | ||
* @copyright 2023 Istituto Italiano di Tecnologia (IIT). This software may be modified and | ||
* distributed under the terms of the BSD-3-Clause license. | ||
*/ | ||
|
||
// Catch2 | ||
#include <Eigen/Dense> | ||
#include <Eigen/src/Core/Matrix.h> | ||
#include <catch2/catch.hpp> | ||
|
||
#include <BipedalLocomotion/System/NamedTuple.h> | ||
|
||
TEST_CASE("Test NamedTuple") | ||
{ | ||
using namespace BipedalLocomotion::System::literals; | ||
using namespace BipedalLocomotion::System; | ||
|
||
auto foo = make_named_tuple(named_param<"name"_h, std::string>(), | ||
named_param<"number"_h, int>(), | ||
named_param<"floating_number"_h, double>(), | ||
named_param<"vector"_h, Eigen::VectorXd>()); | ||
|
||
SECTION("Get") | ||
{ | ||
auto& [a, b, c, d] = foo; | ||
a = "Smith"; | ||
b = 150; | ||
c = 3.14; | ||
d = Eigen::VectorXd::Ones(10); | ||
|
||
REQUIRE(a == foo.get_from_hash<"name"_h>()); | ||
REQUIRE(std::get<0>(foo) == foo.get_from_hash<"name"_h>()); | ||
|
||
REQUIRE(b == foo.get_from_hash<"number"_h>()); | ||
REQUIRE(std::get<1>(foo) == foo.get_from_hash<"number"_h>()); | ||
|
||
REQUIRE(c == foo.get_from_hash<"floating_number"_h>()); | ||
REQUIRE(std::get<2>(foo) == foo.get_from_hash<"floating_number"_h>()); | ||
|
||
REQUIRE(d == foo.get_from_hash<"vector"_h>()); | ||
REQUIRE(std::get<3>(foo) == foo.get_from_hash<"vector"_h>()); | ||
} | ||
|
||
SECTION("Set") | ||
{ | ||
foo.get_from_hash<"name"_h>() = "Olivia"; | ||
REQUIRE(std::get<0>(foo) == foo.get_from_hash<"name"_h>()); | ||
|
||
foo.get_from_hash<"number"_h>() = 42; | ||
REQUIRE(std::get<1>(foo) == foo.get_from_hash<"number"_h>()); | ||
|
||
foo.get_from_hash<"floating_number"_h>() = 8.15; | ||
REQUIRE(std::get<2>(foo) == foo.get_from_hash<"floating_number"_h>()); | ||
|
||
foo.get_from_hash<"vector"_h>() = Eigen::Vector4d::Constant(3); | ||
REQUIRE(std::get<3>(foo) == foo.get_from_hash<"vector"_h>()); | ||
} | ||
} |