Last active
September 2, 2023 23:40
-
-
Save leopoldcambier/1f5b92b0afc8754e40f7faa6b9bd8afc to your computer and use it in GitHub Desktop.
Chaining functions using C++ templates
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
#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"; | |
} |
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
This helped me a lot...! Thanks.
I am from KUL btw : ) glad to see someone from Belgium.