How to defer expanding a parameter pack? - c++17

I was toying around with tuples. I wanted to zip an arbitrary number of tuples. There is probably a better way to do that than what I came up with, but my solution led me to a problem that is interesting in itself.
Sometimes, you want to expand one parameter pack at a time, and those parameter packs can be template arguments and function arguments in the same call. I could not find an obvious way to expand Is without expanding ts, besides stuffing ts into a std::tuple and unpacking it again using std::get<I>.
Obviously, it's preferable not to split one function into five functions. I know I could use lambdas, but I'm not sure that would be any cleaner.
Is there a nice way to defer expanding ts?
https://godbolt.org/z/sY5xMTa7P
#include <iostream>
#include <tuple>
#include <string_view>
template <typename T, typename... Ts>
auto get_first(T t, Ts...) {
return t;
}
template <size_t I, typename... Ts>
auto zip2_impl4(Ts... ts) {
return std::make_tuple(std::get<I>(ts)...);
}
template <size_t I, typename T, size_t... Is>
auto zip2_impl3(T t, std::index_sequence<Is...>) {
return zip2_impl4<I>(std::get<Is>(t)...);
}
template <size_t I, typename T>
auto zip2_impl2(T t) {
using size = std::tuple_size<T>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl3<I>(t, seq{});
}
template <size_t... Is, typename... Ts>
auto zip2_impl(std::index_sequence<Is...> seq, Ts... ts) {
// need to defer expanding the pack ts,
// because the packs Is and ts need to expand separately
auto t = std::make_tuple(ts...);
return std::make_tuple(zip2_impl2<Is>(t)...);
}
template <typename... Ts>
auto zip2(Ts... ts) {
using size = std::tuple_size<decltype(get_first(ts...))>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl(seq{}, ts...);
}
int main() {
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip2(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
}, args), ...);
}, zipped);
return 0;
}
output: 1ad2be3cf

If I understood you correctly:
https://godbolt.org/z/e4fvr45rz
#include <type_traits>
#include <tuple>
#include <cstddef>
template <size_t Indx,typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return std::make_tuple(std::get<Indx>(tuples)...);
}
template <typename ... Tuples, size_t ... Indx>
constexpr auto zip(std::index_sequence<Indx...>, Tuples... tuples)
{
// will expand simultaneously, not what you want...
// return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...));
return std::make_tuple(zip<Indx>(tuples...)...);
}
template <typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return zip(std::make_index_sequence<sizeof...(Tuples)>(), tuples...);
}
#include <iostream>
#include <string_view>
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
std::cout << std::endl;
}, args), ...);
}, zipped);
return 0;
}
Output:
Program returned: 0
1ad
2be
3cf
So there is a way, I guess. You have to have your parameter packs in different contexts: one in the function arguments, another in the template argumetns. If you try to expand both packs in one "context" you will get a compile-time error:
// this will not compile
// return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...)...);
// and this will expand them simultaneously, giving you "1bf" as a result
return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...));
UPDATE:
https://godbolt.org/z/9E1zj5q4G - more generic solution:
template <typename FirstTuple, typename ... Tuples>
constexpr auto zip(FirstTuple firstTuple, Tuples ... tuples)
{
return zip(std::make_index_sequence<std::tuple_size_v<FirstTuple>>(), firstTuple, tuples...);
}
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto svs3 = std::make_tuple("g"sv, "h"sv, "i"sv);
auto zipped = zip(ints, svs1, svs2, svs3);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
std::cout << std::endl;
}, args), ...);
}, zipped);
return 0;
}
Output:
1adg
2beh
3cfi
Instead of number of tuples you pass a number of elements. You have to be sure that each tuple has at least as many elements as the first one. Extra elements will be truncated. If there are too few elements, there will be a compilation error from std::get<>.

Related

why can't I pass an l-value to a template class constructor that uses universal references?

I have a templated class MyClass<T> that takes some iterable containing ints (e.g. T = std::vector<int>) in its constructor and does something with it.
I would like to be able to pass the iterable as either a temporary object (e.g. MyClass(std::vector<int>{3,6,9}) or similar r-value argument) or from a named variable (resulting in an l-value as the constructor argument).
I would like to use C++17 template class inference (i.e. write MyClass(...), not MyClass<std::vector<int>>(...)).
I thought that I could declare the constructor parameter as MyClass(T && vec) (a "universal reference") to take either an l-value or an r-value (just like I can with functions), but it gives an error. It seems like T is always inferred as std::vector<int> and never std::vector<int>& with classes, whereas functions infer std::vector<int>& when the argument is an l-value.
How exactly are the rules for template constructor inference and template function inference different? Can I avoid having to use a wrapper function (e.g. myFunction(T&&vec) { return MyClass<T>(std::forward<T>(vec)); }) just for the sake of template inference?
Run the code below on Godbolt:
#include <iostream>
#include <utility>
#include <vector>
template <typename T>
using BeginType = decltype(std::declval<T>().begin());
template <typename T>
struct MyClass {
BeginType<T> begin;
BeginType<T> end;
MyClass(T && vec) {
begin = std::forward<T>(vec).begin();
end = std::forward<T>(vec).end();
}
int sum() {
int sum = 0;
for (auto it = begin; it != end; ++it) sum += *it;
return sum;
}
};
template <typename T>
MyClass<T> myFunction(T && vec) {
return MyClass<T>(std::forward<T>(vec));
}
int main() {
std::vector<int> x{1, 2, 3};
std::vector<int> y{2, 4, 6};
// Warmup: Passing r-values works fine
std::cout << MyClass(std::vector<int>{3, 6, 9}).sum() << std::endl; // works fine: T is std::vector<int>
std::cout << MyClass(std::move(y)).sum() << std::endl; // works fine: T is std::vector<int>
// Unexpected: Passing l-values doesn't work
// std::cout << MyClass(x).sum() << std::endl; // error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'
// Compare: Passing l-values to function works fine
std::cout << myFunction(x).sum() << std::endl; // works fine: T is std::vector<int>&
}
Add a user-defined deduction guide after the class definition:
template <typename T>
struct MyClass {
// same as in question
};
template <typename TT> MyClass(TT && vec) -> MyClass<TT>;
See also How to write a constructor for a template class using universal reference arguments in C++

Recursive aggregate type configured using many template parameters using std::index_sequence

There is a class template:
template<std::size_t ID, std::size_t T1, std::size_t T2, std::size_t T3>
class Feature { /* Implementation goes here */ };
All the instantiations of Feature<...> are 'collected' here:
template<typename FEATURE, typename... OTHERS>
class Features<FEATURE, OTHERS...> : public Features<OTHERS...> {
public:
/* Operations defined here */
private:
FEATURE m_feature;
};
All the features are created as follows:
using FeatureConfig = Features<Feature<0, 1, 2, 3>, Feature<1, 4, 5, 6>>;
FeatureConfig m_features;
So far so good. My task is to get rid of those hard coded values in there 1..3, 4..6 etc. The way to do so is to have generated header file which contains the configuration for all the features. Something like:
template<std::size_t> struct Config;
template<>
struct Config<0> {
static constexpr std::size_t t1 { 1 };
static constexpr std::size_t t2 { 2 };
static constexpr std::size_t t3 { 3 };
};
template<>
struct Config<1> {
static constexpr std::size_t t1 { 4 };
static constexpr std::size_t t2 { 5 };
static constexpr std::size_t t3 { 6 };
};
Then I need to change type definition of FeatureConfig somehow to use the specializations of FeatureConfig based on an index (0, 1, ...). My unsuccessfull try is:
template<std::size_t... INDEX_SEQUENCE>
using FeatureConfig = Features<Feature<INDEX_SEQUENCE, Config<INDEX_SEQUENCE>::t1, Config<INDEX_SEQUENCE>::t2, Config<INDEX_SEQUENCE>::t3>...>;
FeatureConfig<std::make_index_sequence<2>> m_features;
It seems I am somehow mixing type and value...
Many thanks in advance to anyone willing to help me fix the incorrect code in my last listing up there.
Cheers Martin
If I understand correctly what do you want...
I propose the declaration (no definition required because is used only inside a decltype()) of the following function
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
Now you can define FeatureConfig simply as follows
template <std::size_t N>
using FeatureConfig
= decltype(getFeaturesType(std::make_index_sequence<N>{}));
The following is a full compiling (simplified) example
#include <type_traits>
#include <utility>
template <std::size_t, std::size_t, std::size_t, std::size_t>
struct Feature { };
template <typename...>
struct Features
{ };
template <typename F, typename... Os>
struct Features<F, Os...> : public Features<Os...>
{ F m_feature; };
template <std::size_t N>
struct Config
{
static constexpr std::size_t t1 { N*3u };
static constexpr std::size_t t2 { 1u + N*3u };
static constexpr std::size_t t3 { 2u + N*3u };
};
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
template <std::size_t N>
using FeatureConfig
= decltype(getFeaturesType(std::make_index_sequence<N>{}));
int main ()
{
using T1 = FeatureConfig<2u>;
using T2 = Features<Feature<0u, 0u, 1u, 2u>, Feature<1u, 3u, 4u, 5u>>;
static_assert( std::is_same<T1, T2>::value, "!" );
}
If I understand correctly how do you use Config (if t1 is ever N*3u, if if t2 is ever 1u+N*3u and if t3 is ever 2u+N*3u), you can avoid Config at all and write getFeaturesType as follows
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Is*3u, Is*3u+1u, Is*3u+2u>...>;

Variadic Dispatch Function

I have an interface wherein the types of the parameters mostly encode their own meanings. I have a function that takes one of these parameters. I'm trying to make a function that takes a set of these parameters and performs the function on each one in order.
#include <iostream>
#include <vector>
enum param_type{typeA,typeB};
template <param_type PT> struct Container{
int value;
Container(int v):value(v){}
};
int f(Container<typeA> param){
std::cout<<"Got typeA with value "<<param.value<<std::endl;
return param.value;
}
int f(Container<typeB> param){
std::cout<<"Got typeB with value "<<param.value<<std::endl;
return param.value;
}
My current solution uses a recursive variadic template to delegate the work.
void g(){}
template <typename T,typename...R>
void g(T param,R...rest){
f(param);
g(rest...);
}
I would like to use a packed parameter expansion, but I can't seem to get that to work without also using the return values. (In my particular case the functions are void.)
template <typename...T> // TODO: Use concepts once they exist.
void h(T... params){
// f(params);...
// f(params)...; // Fail to compile.
// {f(params)...};
std::vector<int> v={f(params)...}; // Works
}
Example usage
int main(){
auto a=Container<typeA>(5);
auto b=Container<typeB>(10);
g(a,b);
h(a,b);
return 0;
}
Is there an elegant syntax for this expansion in C++?
In C++17: use a fold expression with the comma operator.
template <typename... Args>
void g(Args... args)
{
((void)f(args), ...);
}
Before C++17: comma with 0 and then expand into the braced initializer list of an int array. The extra 0 is there to ensure that a zero-sized array is not created.
template <typename... Args>
void g(Args... args)
{
int arr[] {0, ((void)f(args), 0)...};
(void)arr; // suppress unused variable warning
}
In both cases, the function call expression is cast to void to avoid accidentally invoking a user-defined operator,.

Storing multiple types into class member container

I was reading this Q/A here and as my question is similar but different I would like to know how to do the following:
Let's say I have a basic non template non inherited class called Storage.
class Storage {};
I would like for this class to have a single container (unordered multimap) is where I'm leaning towards... That will hold a std::string for a name id to a variable type T. The class itself will not be template. However a member function to add in elements would be. A member function to add might look like this:
template<T>
void addElement( const std::string& name, T& t );
This function will then populate the unorderd multimap. However each time this function is called each type could be different. So my map would look something like:
"Hotdogs", 8 // here 8 is int
"Price", 4.85f // here 4.8f is float.
How would I declare such an unorderd multimap using templates, variadic parameters, maybe even tuple, any or variant... without the class itself being a template? I prefer not to use boost or other libraries other than the standard.
I tried something like this:
class Storage {
private:
template<class T>
typedef std::unorderd_multimap<std::string, T> DataTypes;
template<class... T>
typedef std::unordered_multimap<std::vector<std::string>, std::tuple<T...>> DataTypes;
};
But I can not seem to get the typedefs correct so that I can declare them like this:
{
DataTypes mDataTypes;
}
You tagged C++17, so you could use std::any (or std::variant if the T type can be a limited and know set of types`).
To store the values is simple.
#include <any>
#include <unordered_map>
class Storage
{
private:
using DataTypes = std::unordered_multimap<std::string, std::any>;
DataTypes mDataTypes;
public:
template <typename T>
void addElement (std::string const & name, T && t)
{ mDataTypes.emplace(name, std::forward<T>(t)); }
};
int main()
{
Storage s;
s.addElement("Hotdogs", 8);
s.addElement("Price", 4.85f);
// but how extract the values ?
}
But the problem is that now you have a element with "Hotdogs" and "Price" keys in the map, but you have no info about the type of the value.
So you have to save, in some way, a info about the type of th value (transform the value in a std::pair with some id-type and the std::any?) to extract it when you need it.
I've done something along those lines, the actual solution is very specific to your problem.
That being said, I'm doing this on a vector, but the principle applies to maps, too.
If you're not building an API and hence know all classes that will be involved you could use std::variant something along the lines of this:
#include <variant>
#include <vector>
#include <iostream>
struct ex1 {};
struct ex2 {};
using storage_t = std::variant<ex1, ex2>;
struct unspecific_operation {
void operator()(ex1 arg) { std::cout << "got ex1\n";}
void operator()(ex2 arg) { std::cout << "got ex2\n";}
};
int main() {
auto storage = std::vector<storage_t>{};
storage.push_back(ex1{});
storage.push_back(ex2{});
auto op = unspecific_operation{};
for(const auto& content : storage) {
std::visit(op, content);
}
return 0;
}
which will output:
got ex1
got ex2
If I remember correctly, using std::any will enable RTTI, which can get quite expensive; might be wrong tho.
If you provide more specifics about what you actually want to do with it, I can give you a more specific solution.
for an example with the unordered map:
#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
struct ex1 {};
struct ex2 {};
using storage_t = std::variant<ex1, ex2>;
struct unspecific_operation {
void operator()(ex1 arg) { std::cout << "got ex1\n";}
void operator()(ex2 arg) { std::cout << "got ex2\n";}
};
class Storage {
private:
using map_t = std::unordered_multimap<std::string, storage_t>;
map_t data;
public:
Storage() : data{map_t{}}
{}
void addElement(std::string name, storage_t elem) {
data.insert(std::make_pair(name, elem));
}
void doSomething() {
auto op = unspecific_operation{};
for(const auto& content : data) {
std::visit(op, content.second);
}
}
};
int main() {
auto storage = Storage{};
storage.addElement("elem1", ex1{});
storage.addElement("elem2", ex2{});
storage.addElement("elem3", ex1{});
storage.doSomething();
return 0;
}

How to discover lock declaration instruction in llvm?

I'm new to llvm , and was trying to find lock declaration statement and then do some instrumention work,the code like this:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int share = 42;
mutex m;
void f()
{
m.lock();
--share;
cout << "function f -> share: " << share << '\n';
m.unlock();
}
int main()
{
thread thf{f};
thf.join();
return 0;
}
I want to find the lock declaration instruction eg:
mutex m;
the llvm instrumention pass like this:
struct SkeletonPass : public FunctionPass {
static char ID;
SkeletonPass() : FunctionPass(ID) {}
virtual bool runOnFunction(Function &F) {
// Get the function to call from our runtime library.
LLVMContext &Ctx = F.getContext();
Constant *logFunc = F.getParent()->getOrInsertFunction(
"logop", Type::getVoidTy(Ctx), Type::getInt32Ty(Ctx), NULL
);
for (auto &B : F) {
for (auto &I : B) {
***if ((&I) is lock declaration instruction)*** {
// Insert something *after* `op`.
IRBuilder<> builder(op);
builder.SetInsertPoint(&B, ++builder.GetInsertPoint());
// Insert a call to function.
builder.CreateCall(logFunc, ConstantInt::get(Type::getInt32Ty(Ctx), 2));
return true;
}
}
}
In short, could you please tell me how to discover lock declaration instruction, thanks!
The declaration would appear as a global, so you should write a module pass to find it, not a function pass. It should appear as something like:
#m = global %mutex zeroinitializer
In fact, using the demo at http://ellcc.org/demo/index.cgi to try this, you can indeed see that:
...
%"class.std::__1::mutex" = type { %struct.pthread_mutex_t }
%struct.pthread_mutex_t = type { %union.anon }
%union.anon = type { [5 x i8*] }
...
#m = global %"class.std::__1::mutex" zeroinitializer, align 8
You can use LLVM's CppBackend to compile your code. This would produce a C++ code that makes up the source. You can then easily find out how mutex m; definition is constructed via LLVM API.
Run clang -march=cpp foo.cpp to use CppBackend. Alternatively, you can use this demo page to compile your code online.

Resources