Tagged structures casting rules - c++

I was playing around with toy tuple implementations and eventually stuck with how get function works.
Consider this simple example
#include <iostream>
#include <utility>
template <size_t Tag, typename ValueType>
struct TagedValue { ValueType value; };
struct Test : TagedValue<0, int>, TagedValue<1, std::string>, TagedValue<2, double> {};
template <size_t Idx, typename T>
auto& get(Test& test) {
((TagedValue<Idx, T>&)(test)).value;
}
template <size_t Idx, typename T>
auto& get_impl(TagedValue<Idx, T>& tagged_value) {
return tagged_value.value;
}
template <size_t Idx>
auto& get_2(Test& test) {
return get_impl<Idx>(test);
}
int main()
{
Test test;
get_2<0>(test);
get<0>(test);
}
I get this error:
<source>: In function 'int main()':
<source>:29:16: error: no matching function for call to 'get<0>(Test&)'
29 | get<0>(test);
| ^
<source>:10:7: note: candidate: 'template<long unsigned int Idx, class T> auto& get(Test&)'
10 | auto& get(Test& test) {
| ^~~
<source>:10:7: note: template argument deduction/substitution failed:
<source>:29:16: note: couldn't deduce template parameter 'T'
29 | get<0>(test);
| ^
I do have couple of questions:
Basically why get_2 works and get doesn't compile. To me it looks like get_2 does exactly what I'm trying to do inside get
Does deducing T for get_2 take O(1) time, if yes how is it possible? Does compiler store some kind of map internally?

When you call a function template then all template arguments must either be specified explicitly or be deduced from the function arguments. When you call
get<0>(test);
Then Idx is 0, but there is no way for the compiler to know what T is supposed to be. The parameter is just Test, and T cannot be deduced from that.

Related

CTAD doesn't work with defaulted template arguments?

Compare the following case when I have a class object that takes a vector. The non-deduced parameter T can be substituted fine with the default template argument:
#include <vector>
template <typename T = int>
struct container
{
container(std::vector<T> vec) {}
};
int main()
{
container C = std::vector{1,2,3,4,5};
}
This is not the case for my class which is a bit more complicated (CompilerExplorer):
#include <cstdio>
#include <initializer_list>
#include <variant>
template <size_t> struct obj;
template<size_t Vs>
using val = std::variant<std::monostate, int, struct obj<Vs>>;
template <size_t Vs = 0>
struct obj
{
obj() = default;
obj(std::initializer_list<val<Vs>> init) {
printf("initializer of object called, Vs = %d\n", Vs);
}
};
template <size_t Vs = 0>
struct container : public obj<Vs>
{
container(obj<0> init) {}
};
int main()
{
container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
}
This fails with the following error:
<source>: In function 'int main()':
<source>:29:57: error: class template argument deduction failed:
29 | container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
| ^
<source>:29:57: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
But it works when I supplement the template specialization obj<0> in the instantiation of the container (in main). Any ideas why this doesn't work for my class and how I can fix it? I don't want to force the user to specify the template each time.
This problem already exists in the simpler case of just
auto o = obj{1,2,33};
which yields this error:
<source>:29:24: error: class template argument deduction failed:
29 | auto o = obj{1,2,33};
| ^
<source>:29:24: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
<source>:14:5: note: template argument deduction/substitution failed:
<source>:29:24: note: mismatched types 'std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >' and 'int'
29 | auto o = obj{1,2,33};
So, the compiler is unable to deduce, that the three ints should be an initializer list. If you add extra braces around them, the compiler recognizes that this should actually be a single list argument instead of three separate ones and it works:
auto o = obj{{1,2,33}};
This also carries over to the more complicated case:
container some_container = obj{{1,2,5,2,obj{{1,2,33}},2,2}};

"no matching function for call to" when having a function pointer with template arguments as a template argument

I'm writing a template wrapper function that can be applied to a functions with different number/types of arguments.
I have some code that works but I'm trying to change more arguments into template parameters.
The working code:
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS>
int wrapper(int (*func)(ARGS...), ARGS... args) { return (*func)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool>(func0, b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
Now I want int (*func)(ARGS...) to also be a template parameter. (It's for performance reasons. I want the pointer to be backed into the wrapper, because the way I'm using it prevents the compiler from optimizing it out.)
Here is what I came up with (The only difference is I've changed the one argument into a template parameter.):
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS, int (*FUNC)(ARGS...)>
int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
This doesn't compile. It shows:
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
ASM generation compiler returned: 1
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
Execution build compiler returned: 1
(link to the compiler explorer)
It looks like a problem with the compiler to me, but GCC and Clang agree on it so maybe it isn't.
Anyway, how can I make this template compile correctly with templated pointer to a function?
EDIT:
Addressing the duplicate flag Compilation issue with instantiating function template
I think the core of the problem in that question is the same as in mine, however, it lacks a solution that allows passing the pointer to function (not only its type) as a template parameter.
This doesn't work because a pack parameter (the one including ...) consumes all remaining arguments. All arguments following it can't be specified explicitly and must be deduced.
Normally you write such wrappers like this:
template <typename F, typename ...P>
int wrapper(F &&func, P &&... params)
{
return std::forward<F>(func)(std::forward<P>(params)...) * 10;
}
(And if the function is called more than once inside of the wrapper, all calls except the last can't use std::forward.)
This will pass the function by reference, which should be exactly the same as using a function pointer, but I have no reasons to believe that it would stop the compiler from optimizing it.
You can force the function to be encoded in the template argument by passing std::integral_constant<decltype(&func0), func0>{} instead of func0, but again, I don't think it's going to change anything.
The 2nd snippet is not valid because:
a type parameter pack cannot be expanded in its own parameter clause.
As from [temp.param]/17:
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
So consider the following invalid example:
template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
The above example is invalid because the template type parameter pack Ts cannot be expanded in its own parameter list.
For the same reason, your code example is invalid. For example, a simplified version of your 2nd snippet doesn't compile in msvc.

Boost mp11 mp_for_each with additional function argument

I'm trying to pass a list of pairs of types constructed from boost::mp11::mp_product to a function that takes the pairs along with an additional function argument via boost::mp11::mp_for_each.
The docs I've found for mp_for_each are limited to use with generic lambdas or pure functions, so I can't seem to figure out if the use of std::bind is the way to go; and if it is, what I'm doing wrong yielding the following compiler error:
error: no matching function for call to 'bind'
std::bind(inject_foo, m, std::placeholders::_1));
^~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:2953:1: note: candidate template ignored: couldn't infer template argument '_Fp'
bind(_Fp&& __f, _BoundArgs&&... __bound_args)
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:2962:1: note: candidate template ignored: couldn't infer template argument '_Rp'
bind(_Fp&& __f, _BoundArgs&&... __bound_args)
Code I'm using:
#include <pybind11/numpy.h>
#include <boost/mp11.hpp>
#include <functional>
using boost::mp11::mp_product;
using boost::mp11::mp_for_each;
template <typename...> struct type_list {};
// all possible types
using my_type_list = type_list<
double, float, py::ssize_t, int, unsigned int, unsigned long>;
// construct all possible pairs of types with help from boost::mp11
using my_type_pairs = mp_product<
type_list, my_type_list, my_type_list>;
// the C++ function that we bind to a python module in the next function.
template <typename Tx, typename Ty>
py::array<py::ssize_t> foo(p::array_t<Tx> x, py::array_t<Ty>) {
py::array_t<py::ssize_t> z;
// do something with x and y
return z;
}
// bind foo<Tx, Ty> function to py::module m
template <typename Tx, typename Ty>
void inject_foo(py::module_& m, const type_list<Tx, Ty>&) {
m.def("_foo", &foo<Tx, Ty>, py::arg("x").noconvert(), py::arg("y").noconvert());
}
PYBIND11_MODULE(_backend, m) {
// these function calls work as expected:
// inject_foo(m, type_list<double, double>{});
// inject_foo(m, type_list<double, float>{});
// inject_foo(m, type_list<double, int>{});
// .....
// trying to make my life easier with the loop
// over all possible types of pairs is not working
mp_for_each(pg_type_pairs{}, std::bind(inject_foo, m, std::placeholders::_1));
}
PiotrNycz's comment led to an answer:
mp_for_each<pg_type_pairs>([&](const auto& x) { inject_foo(m, x); });
Provides the desired behavior (In the question I was even using the mp_for_each API incorrectly; switching to a lambda helped find that error).

Adding a type to an existing template with out it being dropped c++17

My goal is to be able to include my own extra type at declaration and have it passed to my template function. How would I be able to declare my type so that the compiler would not drop my extra template parameter.
For example I have this code:
#include <iostream>
#include <vector>
// my_vector is an alias for std::vector<T> that also takes an extra type E
template<typename T, typename E>
using my_vector = std::vector<T>;
// my aliased type is being demoted to std::vector<T, std::allocator<T> >
template<typename T, typename E>
void write_to(std::ostream stream, const my_vector<T, E>& vec) {
// I need E type for somthing here for example this
stream << static_cast<E>(vec.size());
for (auto elm : vec) {
stream << elm;
}
}
int main() {
// very redundantly declaring that I want my_vector
my_vector<float, uint8_t> vec = my_vector<float, uint8_t>{ 1.0f, 2.0f, 3.0f };
write_to(std::cout, vec);
// this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
}
g++ output suggests that it is not passing my_vector<T, E> to write_to but instead drops my_vector altogether and instead passes std::vector<T, std::allocator<T> >, is it possible to get the compiler to not drop the extra template parameter so that I dont have to explicitly include it in every call of write_to here?
Here is my output from g++ std=c++17
[arkav:~/devel/packetize] $g++ template_demote.cc --std=c++17
template_demote.cc: In function ‘int main()’:
template_demote.cc:21:25: error: no matching function for call to ‘write_to(std::ostream&, my_vector<float, unsigned char>&)’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
template_demote.cc:10:6: note: candidate: ‘template<class T, class E> void write_to(std::ostream, my_vector<T, E>&)’
10 | void write_to(std::ostream stream, const my_vector<T, E>& vec) {
| ^~~~~~~~
template_demote.cc:10:6: note: template argument deduction/substitution failed:
template_demote.cc:21:25: note: couldn’t deduce template parameter ‘E’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
Solution
Inherent std::vector and its constructor in my type definition
template<typename E, typename T>
class my_vector: public std::vector {
using std::vector<T>::vector;
};
You can try something like this:
#include <iostream>
#include <vector>
template<class T, class E>
struct util {
std::vector<T> my_vector;
void write_to(std::ostream& stream) {
stream << static_cast<E>(my_vector.size());
for (auto elm : my_vector) {
stream << elm;
}
}
};
int main() {
util<float, uint8_t> u;
u.my_vector = std::vector<float>{ 1.0f, 2.0f, 3.0f };
u.write_to(std::cout);
}
You can read in cppreference that, when aliasing:
Alias templates are never deduced by template argument deduction when deducing a template template parameter.

using std::result_of to determine the return type of a template argument

I think the snippet of code is self explanatory, but basically the template function ExecFunc should be able to execute another function and return its result. I know I can achieve similar results using decltype instead of result_of, but this question is to understand why what I've written does not work: the snippet does not compile on gcc v4.9.2.
This is what I have:
#include <type_traits>
int f(int i)
{
return i;
}
template<class F, class T>
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
{
return f(arg);
}
int main() {
auto a = ExecFunc(f, 3);
return 0;
}
and this is the compiler output:
prova.cpp: In function ‘int main()’:
prova.cpp:15:26: error: no matching function for call to ‘ExecFunc(int (&)(int), int)’
auto a = ExecFunc(f, 3);
^
prova.cpp:15:26: note: candidate is:
prova.cpp:9:6: note: template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T)
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
^
prova.cpp:9:6: note: template argument deduction/substitution failed:
prova.cpp: In substitution of ‘template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T) [with F = int (*)(int); T = int]’:
prova.cpp:15:26: required from here
prova.cpp:9:6: error: no type named ‘type’ in ‘class std::result_of<int (*())(int)>’
N.B.
this question might look like a duplicate of this one but the accepted solution doesn't work for me (at least, as far as I can tell I have incorporated the solution in my code).
The function you have is int f(int i) but you are calling F() which is unknown. std::result_of<F()>::type should be std::result_of<F(T)>::type.
Live Example
The problem is with the parameter of result_of, it should be:
-> typename std::result_of<F(T)>::type
This is the perfect time to use decltype
template<class F, class T>
auto ExecFunc(F f, T arg) -> decltype(f(arg))