Skip to content

Commit

Permalink
[TODO] Implement the first version of tagged tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
GiulioRomualdi committed Mar 31, 2023
1 parent 554aad0 commit a555a30
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/System/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ if(FRAMEWORK_COMPILE_System)
${H_PREFIX}/QuitHandler.h
${H_PREFIX}/Barrier.h
${H_PREFIX}/WeightProvider.h ${H_PREFIX}/ConstantWeightProvider.h
${H_PREFIX}/NamedTuple.h
SOURCES src/VariablesHandler.cpp src/LinearTask.cpp
src/StdClock.cpp src/Clock.cpp src/QuitHandler.cpp src/Barrier.cpp
src/ConstantWeightProvider.cpp
Expand Down
270 changes: 270 additions & 0 deletions src/System/include/BipedalLocomotion/System/NamedTuple.h
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
5 changes: 5 additions & 0 deletions src/System/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ add_bipedal_test(
SOURCES VariablesHandlerTest.cpp
LINKS BipedalLocomotion::System)

add_bipedal_test(
NAME NamedTuple
SOURCES NamedTupleTest.cpp
LINKS BipedalLocomotion::System)

add_bipedal_test(
NAME AdvanceableRunner
SOURCES AdvanceableRunnerTest.cpp
Expand Down
60 changes: 60 additions & 0 deletions src/System/tests/NamedTupleTest.cpp
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>());
}
}

0 comments on commit a555a30

Please sign in to comment.