How can I reduce the size of an array argument? - c++

I've got a template function, bar, that takes a reference to an array as a parameter. I'd like to take the argument and pass it to another function, but only after reducing the size of the array by one, and skipping past the first element of the array.
#include <iostream>
template <typename T>
void foo(const T& t)
{
std::cout << sizeof(t) << std::endl;
std::cout << t[0] << std::endl;
}
template <typename T>
void bar(const T& t)
{
std::cout << sizeof(t) << std::endl;
std::cout << t[0] << std::endl;
foo(t); // Magic is desired here!
}
int main()
{
char array[] = "ABCD";
bar(array);
}
The above prints out:
5
A
5
A
I'd like it to print out:
5
A
4
B

You can do this with two template parameters, one for the array type and one for the size of the array.
template <typename T, int N>
void bar(const T (&t)[N])
{
// ...
foo(reinterpret_cast<const T(&)[N-1]>(t[1]));
}

Copying the array may be necessary to get a reference. I hope this answer will draw attention to the real subject of your question..
Calling foo with an appropriate-looking (and generic) cast looks as follows
reinterpret_cast<typename std::remove_reference<decltype(t[0])>::type [sizeof(t)-1]>(t+1)
but the above is invalid - you cannot cast const char* to const char[4]; Also you cannot obtain a reference in another way since an array cannot be copy constructed. So you may need to either copy or use std::array in c++11, which really boils down to having two template parameters.
Here is a valid solution however:
typedef typename std::remove_reference<decltype(t[0])>::type element_type;
foo(reinterpret_cast<element_type(&) [sizeof(T)-1]>(t[1]));

Related

Is there a way to get the type of a dereferenced value from a dereference-able type?

I'm playing around with C++ concepts and came across an interesting problem. I have the following two custom-defined concepts:
template<typename T>
concept is_dereferencable = requires (T t) { *t; };
template<typename T>
concept is_printable = requires (T t) { std::cout << t; };
As the names suggest, the first one is used to determine if a given type can be dereferenced, while the other one to check if a type supports output operator. I also have a function template called println, which looks like this:
template<typename T>
void println(const T& t)
{
if constexpr (is_dereferencable<T>) {
if constexpr (is_printable<decltype(*t)>) {
std::cout << *t << '\n';
}
} else if constexpr (is_printable<T>) {
std::cout << t << '\n';
}
}
This prints the dereferenced value *t if and only if type T is dereference-able and the type of the dereferenced value is printable. So, for example, I can use this function template with something like an std::optional:
int main
{
std::optional<std::string> stringOpt {"My Optional String"};
::println(stringOpt);
return 0;
}
This will print My Optional String as expected. While this is nice, the function will just silently print nothing if the type of a dereferenced value of a derefernce-able is not printable. So, for a user-defined type Person, the following will just print nothing:
struct Person
{
std::string m_name;
explicit Person(const std::string& name) : m_name {name} {}
};
int main
{
std::optional<Person> personOpt {"John Doe"}
::println(personOpt);
return 0;
}
So I would like to move the above compile-time ifs to a requires clause itself in order to get compile time errors in such cases. Is there a way to achieve that? Is there a way to get the dereferenced type of a given template type T? To make it a bit clearer, I would like to have something like this:
template<typename T>
requires is_dereferencable<T> && is_printable<decltype(*T)>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
P.S.: I understand that I could remove the nested if and just fail upon trying to call an output operator on something that doesn't support it. However, I want to specifically move this compile-time error to the concept to get a clearer error message.
another option is put the constraint after parameters, so it has access to them.
template<typename T>
void printDereferencable(const T& t)
requires is_dereferencable<const T&> && is_printable<decltype(*t)>
{
std::cout << *t << '\n';
}
note: it should test on const T&, not T
you can use std::declval
template<typename T>
requires is_dereferencable<const T&> && is_printable<decltype(*std::declval<const T&>())>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
or you can just write a is_dereference_printable concept
template<typename T>
concept is_dereference_printable = requires (T t) { std::cout << *t; };

C++ different using declarations for different concepts

Let's say, I have my List<T> class. I have a lot of functions where I have to pass a single object of my T type. For instance
void add(const T& item)
{
...
}
and it makes sense if T is some class or a struct. However, if T is a byte or integer, it's pointless or even wrong to pas it via reference, since memory pointer costs 8 bytes (4 on 32 bit system), i.e. I pass 1 byte size data type via 8 byte size pointer.
And so I decided to define argument data type using using directive. Kind of:
using argType = const T&; requires sizeof(T) > 8
using argType = T; requires sizeof(T) <= 8
But, obviously, this code doesn't work. Can you, please, propose me other solutions for that?
It sounds like what you need is conditional_t:
#include <type_traits>
template<class T>
class List {
using argType = std::conditional_t<(sizeof(T) > 8), const T&, T>;
void add(argType item) { }
};
To add to the answer provided here: https://stackoverflow.com/a/69864339/2963099 where conditional_t was suggested
It is important to make sure that items with non-trivial copy constructors are passed by reference, since a value copy can be very expensive. As such I would limit pass by value to trivially copyable objects.
Also using sizeof(T*) will work more generically than a hardcoded 8
Note that args are reversed and the test is to use pass by value, and objects with sizeof == 8 can still be pass by reference if needed.
using argType = std::conditional_t<
(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>,
T, const T&>;
An example of usage:
#include <type_traits>
#include <vector>
#include <iostream>
struct Test
{
Test() : x{0}
{
std::cout << "Default construct" << std::endl;
}
Test(const Test& rhs) : x{rhs.x}
{
std::cout << "copy: " << x << std::endl;
}
int * x;
};
template<class T>
struct List {
using argType = std::conditional_t<(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>, T, const T&>;
static argType copy(argType item)
{
std::cout << "Running Copy" << std::endl;
return item;
}
};
auto foo(typename List<Test>::argType t)
{
return List<Test>::copy(t);
}
auto goo(typename List<unsigned long long>::argType t)
{
return List<unsigned long long>::copy(t);
}
int main()
{
Test t;
std::cout << "Calling Test Copy" << std::endl;
foo(t);
std::cout << "After Test Copy" << std::endl;
unsigned long long u;
std::cout << "Calling ULL Copy" << std::endl;
goo(u);
std::cout << "After ULL Copy" << std::endl;
}
see it here: https://godbolt.org/z/WTKT8G6rh
Note, you can see the parameter of foo() and goo() easily here
Note how Test would have been passed by value and incurred a possibly expensive copy, but checking for an expensive copy prevents this
With c++20 concepts you can add some constraints to your templates,
from en.cppreference.com/w/cpp/language/constraints
Class templates, function templates, and non-template functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.
Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint:
#include <string>
#include <cstddef>
#include <concepts>
// Declaration of the concept "Hashable", which is satisfied by any type 'T'
// such that for values 'a' of type 'T', the expression std::hash<T>{}(a)
// compiles and its result is convertible to std::size_t
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}
//
// Alternative ways to apply the same constraint:
// template<typename T>
// requires Hashable<T>
// void f(T) {}
//
// template<typename T>
// void f(T) requires Hashable<T> {}
int main() {
using std::operator""s;
f("abc"s); // OK, std::string satisfies Hashable
//f(meow{}); // Error: meow does not satisfy Hashable
}
In your case I think you can constraint your template as follows:
template<typename T>
concept InfTo8Bytes = requires(T a) {
sizeof (T) <= 8;
};
and use you concepts as follows:
template<InfTo8Bytes T>
class Foo {
};

Template Overloading Behaves Differently Inside a Template Class

In the following code yields (LiveExample):
Class template pointer
Class template pointer
Template pointer
Template array with size 10
#include <iostream>
#include <utility>
#include <type_traits>
template <class TVar>
class CVar
{
public:
void classTemplate(TVar *) const
{
std::cout << "Class template pointer\n";
}
template<unsigned sz>
void classTemplate(TVar(&)[sz]) const
{
std::cout << "Class template array with size " << sz << "\n";
}
};
template<typename T>
void regTemplate(T)
{
std::cout << "Template pointer\n";
}
template<typename T, unsigned sz> void regTemplate(T(&)[sz])
{
std::cout << "Template array with size " << sz << "\n";
}
int main()
{
unsigned int test[10] = {};
CVar<unsigned> *cFoo = new CVar<unsigned>();
cFoo->classTemplate(&test[0]);
cFoo->classTemplate(test);
regTemplate(&test[0]);
regTemplate(test);
}
Why when I overload the template in the class, it cannot resolve the desired functionality - that is, cFoo->classTemplate(test); is called, then the response will be Class template array with size 10?
How do I achieve my desired result in 1 without changing cFoo->classTemplate(test);? Note that void CVar<TVar>::classTemplate(TVar(&)[sz]) const may change if needed
Other things equal, overload resolution prefers non-templates over templates. In classTemplate case, one overload is a function template while the other is a non-template member function.
In regTemplate case, both are function templates: the one taking an array is chosen because it's more specialized. Indeed, if you change the first overload to take T*, the call becomes ambiguous.

Recursive templates - constant n size array -> n-1 size in-place?

This may be a bizarre question, but I have a recursive template that expects an array of size d (where d is a template parameter), and I want to pass the d-1 template the same array as if it was one element shorter. This way I only work with one array, instead of creating a new one for each level.
I feel like the answer may be something very basic, but I can't come up with any search terms that result in anything close to what I'm looking for.
To put this into context, here's an example
template<int d>
void Function(int array[d])
{
array[d- 1]= d;
Function<d- 1>(?);
}
This answer is for static, C-style arrays, If your question is about std::Array, I apologize.
Off the top of my head, I came up with two ways to do the recursion, but many more techniques exist.
The first one uses a partially specialized class (with array count of zero) to terminate the recursion.
The second way uses a cast to a statically-chosen type which ends the recursion with an overloaded function. Here, I cast the array to void*, but for types that won't work with this, you could create a custom type which is constructible from the original type.
I resorted to using reinterpret_cast to change the array's type from a reference to array[count] to array[count-1]. Although I expect this to be safe as it is used here, keep in mind that you might run into problems in different situations.
#include <iostream>
// Ends recursion with a partial specialization
template <typename T, int count>
struct StaticArrayDump {
static void func(T(&a)[count]) {
using shorter_t = T(&)[count-1];
StaticArrayDump<T, count-1>::func(reinterpret_cast<shorter_t>(a));
std::cout << a[count-1] << ' ';
}
};
template <typename T>
struct StaticArrayDump<T,0> {
static void func(...) {}
};
template <typename T, int count>
static void static_array_dump_spec(T(&a)[count]) {
using shorter_t = T(&)[count-1];
StaticArrayDump<T,count>::func(a);
}
// Ends recursion with void* cast and function overload
// Ultimately relies on type_select's specialization, however
template <bool, typename A, typename B> struct type_select /* true */ { using type = A; };
template <typename A, typename B> struct type_select<false,A,B> { using type = B; };
template <bool cond, typename A, typename B>
using type_select_t = typename type_select<cond, A, B>::type;
static void static_array_dump_ovld(...) {}
template <typename T, int count>
static void static_array_dump_ovld(T(&a)[count]) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
static_array_dump_ovld(reinterpret_cast<
type_select_t<next_count!=0, shorter_t, void*>
>(a));
// output the last element
std::cout << a[count-1] << ' ';
}
// This is an overload-based version which is free of
// any reliance on template specialization.
// helper_trueol's (void*, void*) overload will only be
// selected for arguments (array_ref, count) when count
// is 0, because 0 is the only integer which can be
// converted to a pointer.
// This one's compiler compatibility is a bit shaky...
// MSVC 2013 OK
// IdeOne g++ needs int cast for next_count
static void helper_trueol(void*, void*) {}
template <typename T, int count>
static void helper_trueol(T(&a)[count], int) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
helper_trueol(reinterpret_cast<shorter_t>(a), int(next_count));
std::cout << a[count-1] << ' ';
}
template <typename T, int count>
static void static_array_dump_trueol(T(&a)[count]) {
helper_trueol(a, count);
}
// Finally, this overload-based version relies
// on SFINAE to disqualify the template function
// as a candidate when count is 0 because the
// zero-length array type triggeres a substitution
// failure.
// So just using this template array argument type,
// the same one used in all of the previous examples,
// but without any extra mechanisms, is all you need
// to end this recursion!
// This is the obvious best way, of course.
static void static_array_dump_sfinae(...) {}
template <typename T, int count>
static void static_array_dump_sfinae(T(&a)[count]) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
static_array_dump_sfinae(reinterpret_cast<shorter_t>(a));
std::cout << a[count-1] << ' ';
}
//////
int main() {
double dbl_array[] = { 0, 1.2, 3.4, 5.6789, 10 };
static_array_dump_spec(dbl_array);
std::cout << '\n';
const char* cstr_array[] = { "zero", "one", "two", "three", "four" };
static_array_dump_ovld(cstr_array);
std::cout << '\n';
char charray[] = "Hello";
charray[sizeof(charray)-1] = '!'; // replace nul terminator
static_array_dump_trueol(charray);
std::cout << '\n';
bool barray[] = {true, true, true, false, true, false, false, false};
std::cout << std::boolalpha;
static_array_dump_sfinae(barray);
std::cout << '\n';
}
Hopefully I am interpreting this correctly but, when you pass the array as a template, I assume you pass an argument for the size of the array. (otherwise how would you know how large the array is?) When you pass the array, you are passing a pointer to the first element of the array, so when you pass the sub array you could just pass a pointer to the next element and size d-1 or an iterator that points to the next element and size d-1.
Example:
template< typename T>
T foo(T * ptr, int size) {
if (size > 0)
return *ptr + foo(ptr + sizeof(T), size - 1);
else
return 0;
}
From the information that you provided i assume you want too know how too "stop" the recursion. This would look something like this:
// this is the function that will be called from the user, it would be bad design too have to pass an integral constant manually when we can easily do this
template <std::size_t I>
inline
void Function(int (&_array)[I])
{
Function(_array, std::integral_constant<std::size_t, I>);
}
// function will recursively do something with an array for each of it's elements
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, I>)
{
// do something...
Function(_array,std::integral_constant<std::size_t,I-1>);
}
// function as before with a few modifications
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, 1>)
{
// do something...
// exit function...
}

Passing by value all types but string to a template function

I want to define a template function that gets one argument passed by value for all types but std::string (and const char*).
template<typename T>
void foo( T value )
{
// some code using value
}
The std::string version should behave exactly as the template version, but have its parameter passed by const&.
What is the best approach to do what I want without duplicating the body of foo()?
The best I was able to think is to wrap the code using value inside another function, and then call it inside all versions of foo() (the template version and the std::string overload). Is there another way? For example, is it possible to call the template version from within the std::string overload?
EDIT
What I want to know is a good rule of thumb for avoiding code duplication among various specializations and overloads. What is a good pattern to follow? Shall I define a wrapper function for the body and then call that from within all overloads/specializations, or there is another way?
In order to avoid code duplication, the answer by 101010 can be extended to actually call the template from within the overload:
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
output
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
live example
Simple solution: provide an overload for std::string:
void foo( std::string const &value ) {
// some code using value
}
I think what you are looking for is rvalue signature in C++ 11.
Its as simple as:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
You can either pass the parameter by reference or by value and the rvalue rules will use the appropriate type.
You can define a type-trait-like class that will convert std::string to std::string& and will keep the type for all other types:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
Full example: http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD: as noted by #PiotrSkotnicki, having only one parameter makes type-deduction fail. However, I will keep the answer as it might be helpful in case you indeed have several parameters of type T or if you are ok with specifying explicit template parameter to foo.
UPD2: To solve the type-deduction problem, you may add another wrapper:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
This still might have some problems, so whether this is applicable to your usecase, is up to you to decide.
use std::enable_if + std::is_convertibale:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}