Skip to content

Instantly share code, notes, and snippets.

@leopoldcambier
Last active September 2, 2023 23:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leopoldcambier/1f5b92b0afc8754e40f7faa6b9bd8afc to your computer and use it in GitHub Desktop.
Save leopoldcambier/1f5b92b0afc8754e40f7faa6b9bd8afc to your computer and use it in GitHub Desktop.
Chaining functions using C++ templates
#include<tuple>
#include<iostream>
#include<string>
// Returns the Tin of the first element
template<typename... Trs>
struct Tin_list
{
using type = typename std::tuple_element<0, std::tuple<Trs...>>::type::Tin_type;
};
// Returns the Tout of the last element
template<typename... Trs>
struct Tout_list
{
using type = typename std::tuple_element<sizeof...(Trs)-1, std::tuple<Trs...>>::type::Tout_type;
};
// Base case; only one transformator
template<typename Tr>
typename Tr::Tout_type chain_transform(typename Tr::Tin_type tin, Tr transformer)
{
return transformer.transform(tin);
}
// Recursive case
// enable_if will enable this function if Trs... has only one element. Otherwise it will call the above base case
template<typename Tr, typename... Trs>
typename std::enable_if<(sizeof...(Trs) > 0), typename Tout_list<Tr,Trs...>::type>::type
chain_transform(typename Tin_list<Tr,Trs...>::type tin, Tr transformer, Trs... transformers)
{
auto out = transformer.transform(tin);
return chain_transform(out, transformers...);
}
// Expand a tuple into variadic arguments, and apply chain_transform
// Yes it's complicated
template <typename Tin, typename Trs, size_t... Is>
constexpr auto chain_transform_helper(Tin &tin, Trs &trs, std::index_sequence<Is...>)
{
return chain_transform(tin, std::get<Is>(trs)...);
}
template <typename Tin, typename Trs>
constexpr auto chain_transform_tuple(Tin &tin, Trs &trs)
{
return chain_transform_helper(tin, trs, std::make_index_sequence<std::tuple_size<Trs>::value>{});
}
// Creates a chained_transformer from multiple transformers
template<typename... Trs>
struct chained_transformer {
// Tin/Tout types
using Tin_type = typename Tin_list<Trs...>::type;
using Tout_type = typename Tout_list<Trs...>::type;
// Store the transformers
std::tuple<Trs...> transformers;
// Buid
chained_transformer(Trs... trs) : transformers(std::tuple<Trs...>(trs...)) {};
// Apply
Tout_type transform(Tin_type tin) {
return chain_transform_tuple(tin, transformers);
}
};
template<typename... Trs>
auto make_chained_transformer(Trs... trs) {
return chained_transformer<Trs...>(trs...);
};
// Example
struct transformer_0 {
std::string transform(int i) {
return "coucou" + std::to_string(i);
}
using Tin_type = int;
using Tout_type = std::string;
};
struct transformer_1 {
double transform(std::string s) {
return (double) s.size();
}
using Tin_type = std::string;
using Tout_type = double;
};
struct transformer_2 {
size_t transform(double v) {
return (size_t)v + 123;
}
using Tin_type = double;
using Tout_type = size_t;
};
int main() {
auto t0 = transformer_0();
auto t1 = transformer_1();
auto t2 = transformer_2();
std::cout << chain_transform(145, t0, t1, t2) << "\n";
auto transformer = make_chained_transformer(t0, t1, t2);
std::cout << transformer.transform(145) << "\n";
}
@nicksuh
Copy link

nicksuh commented Aug 19, 2022

This helped me a lot...! Thanks.
I am from KUL btw : ) glad to see someone from Belgium.

@leopoldcambier
Copy link
Author

This helped me a lot...! Thanks. I am from KUL btw : ) glad to see someone from Belgium.

Haha :) Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment