String literals and multiple versions of a template function - c++

Let’s say I have a templated function:
template<typename... Args>
void my_log_function(Args&&... args)
{
// Work, work, work.
}
And then invoke it as:
my_log_function("1");
my_log_function("22");
my_log_function("333");
my_log_function("4444");
my_log_function("55555");
Since the type of a string literal is const char[n], I believe this will create a lot of instantiations of my_log_function (Visual Studio Intellisense seems to suggest this). Has anyone observed this as a problem? Can it be? Can it be prevented?

Yes, this should create a different instance of class template. They should only differ in the size of the array passed by reference and if they array isn't really used any further as array, the generated code should be identical. As far as I know, VC++ folds identical code. If the type is propagated a lot, it may yield code bloat, however.
One way to prevent the code bloat is to conditionally decay types and forward to another template doing the actual work. I think this should do the trick:
template <typename... Args>
void my_real_log_function(Args&&... args) {
// real implementation goes here
}
template <typename T>
struct decay_array {
typedef typename std::condition<
std::is_array<typename std::remove_reference<T>::type>::value,
typename std::decay<T>::type,
T>::type type;
};
template <typename... Args>
void my_log_function(Args&&... args) {
my_real_log_function(std::forward<typename std::decay_array<Args>::type>(args)...);
}
The original version just using typename std::decay<T>::type doesn't work because it would turn references to non-arrays into values which isn't intended.

The solution is not to implement "work, work, work" in such a function. Sanitize the variable arguments into something more canonical and hand the work to a non-template.
Mind the bloat and remember that the template system is designed to generate inline functions.

Related

Derive earlier template parameters when later parameter given

Given
template <typename S, typename T>
T make_T(S const &s) { ... }
How can I leave S to be derived while explicitly providing T?
I would like to be able to say:
auto t = make_T<auto, int>(S{});
but clang and gcc tell me that auto is not allowed in template argument.
Had the arguments happened to be reversed in the prototype of make_T,
then all would be well;
I could explicitly give T and leave S to be derived.
In a previous question,
the proposed solution was to declare a helper function that reversed the arguments, e.g.,
template <typename T, typename S>
T make_T_reversed(S const &s) { return make_T<S,T>(s); }
which now enables
auto t = make_T_reversed<int>(S{});
as desired, but I'm hoping there might be a more direct way that doesn't require creating temporary helper functions. I'm asking as a new question because the accepted answer of the previous question doesn't answer my actual question:
is there a direct means of achieving this?
I'm feeling hopeful that with C++17 and C++20 (not around at the time of the previous question), there may now be, but I've sadly been unable to find it.
Further motivating examples
The use case initially motivating the question was that I wanted to write
std::unordered_set<T, default, default, Allocator> obj;
using the default values for the middle two template parameters
(Hash and KeyEqual),
but explicitly specifying the Allocator parameter.
I'm using the default constructor, so the type for Allocator cannot be derived.
I realise the question I actually asked isn't quite the same (I asked about deriving the values rather than taking the default values), but I'm hoping the same approach would work for both cases:
auto t = make_T<auto, int>(S{});
std::unordered_set<T, auto, auto, Allocator> obj;
if S is from T when T is provided, what you will provide for S?
make_T<auto, int>() is definitely impossible, but make_T<void, int>() may be acceptable for you?
template<typename nS, typename T, typename S = std::conditional_t<std::is_same_v<void, nS>, T, nS>>
T make_T(S const&);
but S is always deduced by the argument, why do you want S to be the first parameter?
or you want S to be determined?
template<typename nS, typename T>
T make_T(std::conditional_t<std::is_same_v<void, nS>, T, nS> const&);

Way to pass argument to template function

Lets assume I have a template function
template <typename T>
void do_sth(T const&);
For some types ("small" and copyable) it would be better to pass an argument by value instead of reference.
So my question is: What is the simplest way to overload a function depending on the underlying type?
Remark:
Best what I came to is using enable_if with some conditions of "simple" type. And I believe there's no such a type trait as "simple type" in the standard library. Correct me if I'm wrong. Moreover: Using enable_if gets complicated as a function takes more template arguments (edited) because template <typename T, typename U> void do_sth(T, U) would need 4 overloads: (value, value), (value, ref), (ref, value) and (ref, ref).
Don't do this. Template functions are inlined at the drop of a hat, and a reference to an x is an alias with no identity once the function is inlined.
Encourage the function to be inlined instead of doing a mess of SFINAE, unless and until you have proven this to be an important bottleneck.
After discovering this function is taking up more time than anything else you can optimize, test its improvement by manualy writing the by value version in a couple of key cases to ensure you actually get a benefit (I doubt you will).
boost has call_traits:
template <typename T>
void do_sth(typename boost::call_traits<T>::param_type)
But one big issue is that T is no longer deducible
(and so you have to call it do_sth<int>(42) or do_sth<MyBigObj>(myBigObj)).
So might be used for non template methods in template class:
template <typename T>
struct S
{
void do_sth(typename boost::call_traits<T>::param_type);
};
But anyway, chances are that compiler actually inlines code, resulting in same generated code.

SFINAE to make base template always result in error

So I'm designing a sort of my_numeric_cast function to limit the types of conversions available when using a framework I'm writing.
It was pretty straight forward to do something like
template<typename To, typename From>
constexpr To my_numeric_cast(From);
template<>
constexpr float my_numeric_cast<float, int>(int i) { return i; }
Which works, allowing only casting from ints to floats whenever the cast is used. And producing a linkage error whenever a cast not in the white list is attempted.
However, I'd really want to make this a compilation error, to catch the misuse much faster.
How do I make the base template body valid, expect when instantiating it?
You cannot write a template function specialization for which no template argument makes the body valid in C++. The result if you do so is an ill formed program with no diagnostic required. This includes the primary specialization.
So most of the answers here are simply undefined behaviour. They may work, but they are not valid C++. They may work today, but after a library upgrade a compiler upgrade or a different build target they could fail in completely different and surprising ways. Relying on UB with no strong reason is a bad idea.
On the plus side, we can do away with template specialization and fix your problem in one fell swoop:
template<class T>struct tag_t{}; // may need `constexpr tag_t(){}` on some compilers
template<class T>constexpr tag_t<T> tag{};
template<class T, class F>
constexpr T my_numeric_cast(F, tag_t<F>)=delete; // generates compile time error
constexpr float my_numeric_cast(int i, tag_t<float>) { return i; } // not a template! Could be if you want it to be.
template<typename To, typename From>
constexpr To my_numeric_cast(From f){
return my_numeric_cast(f, tag<To>);
}
and done.
=delete generates friendly messages. Program is well formed. Implementing casts is no longer a specialization. You can even implement it in the namespace of a type being cast to or from as ADL is enabled.
If you solve a problem with template function specialization, reconsider. They are fragile, do not work like class template specialization or function overloading (while looking like both of them!), and usually are not the best solution to anything. There are exceptions when it may be a good idea, but they are quite rare, and given how rare they are avoiding the quirky feature may still be worth it.
You can use traits to get a compile time error:
template<typename, typename> struct RetTraits;
// Enable it for int -> float
template<> struct RetTraits<int, float> { using type = float; };
template<typename To, typename From>
using RetType = typename RetTraits<To, From>::type;
template<typename To, typename From>
constexpr RetType<To, From> my_numeric_cast(From f) {
return To(f);
}
int main() {
my_numeric_cast<int, float>(42);
// This won't compile
// my_numeric_cast<int, int>(42);
}

Template variadic argument referenced from another argument

I'm in trouble for the way I should reference arguments in a template (honestly, I strongly suspect that it's not possible to achieve what I'd like to do).
It follows an example of what I'd like to do (of course, this is not syntactically legal, the aim is to give the idea of which is the target):
template<class C, Ret(C::*Member)(Params...), typename Ret, typename... Params>
class MyClass { }
In other terms, I'd like to reference a member of a class, by specifying at the same time also which is the returned value and the parameters of that method.
Unfortunately, the only way I see to do that is something like the following one (well, it depends indeed on where those typenames are required, anyway it may be a meaningful example):
template<typename Ret, typename... Params>
class MyClass {
public:
template<class C, Ret(C::*Member)(Params...)>
MyClass(C *c) { /* do something else and give sense to this class */ }
}
Besides the one above, that is to break the interlacing by introducing a templated constructor, there exists another valid approach to obtain the same result with the sole class template signature?
I know (pretty simple) how to achieve that in case of not variadic template (as an example, move Ret before Member), but the variadic one (Params) has to lay at the end of the template list and I cannot refer it in any way.
According with this question, a viable solution could be to rely on deduction forced by a default value.
As an example, the following code should be valid:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...) = &C::foo>
void bar(C *c) { }
};
I cite part of the linked question (a citation that is a citation for itself, I'm in a loop):
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument.
Because of that, the following code should not be allowed instead, even if it compiles with GCC:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...)>
void bar(C *c) { }
};
Well, quite tricky, not so flexible a solution and honestly I ended up far ago with a bit of refactoring, but for the sake of clarity I've decided to add an answer and close the question with a snippet that compiles.

Restricting the size of an array when passed to a function

Is there anyway to restrict the size of an array when passed as an argument to a function?
I mean is something like this possible?
/*following will lead to compile time error */
template<typename T, size_t n>=20> // or template<typename T,size_t n<=20>
void func(T (&a)[n])
{
// do something with a
}
I want the size of my array to be at least(or at most) n(n can have any value).
For example:
When n=20 I must pass an array with at least(or at most) 20 elements. Is there any way in C++ for this?
You can simply make the requirement a static assertion - e.g. with Boosts static assert:
template<typename T, size_t n>
void func(T (&a)[n]) {
BOOST_STATIC_ASSERT(n >= 20);
// ...
}
A basic custom implementation (not solving the problem of using it more then once per scope) might look like the following:
template<bool b> struct StaticAssert;
template<> struct StaticAssert<true> {};
#define STATIC_ASSERT(x) StaticAssert<(x)> static_asserter
If you want different behaviour if the requirement is met, use something like enable_if or tag-based specialization. Example using enable_if:
template<class T, size_t n>
typename boost::enable_if<(n >= 20), void>::type
func(T (&a)[n]) { /* ... */ }
template<class T, size_t n>
typename boost::disable_if<(n >= 20), void>::type
func(T (&a)[n]) { /* ... */ }
GF's answer is correct, if you want more features or decisions points at compile time, you might want to look at boost::mpl. C++ Template Metaprogramming outlines what is possible with boost::MPL and how it was designed. Modern C++ design, which is not related to MPL, goes into design techniques which leverage compile time polymorphism
GF's answer is pretty cool, but Boost.Array can't go without mention.
template< typename T >
void func( boost::array< T, 20 > &a ) {
Given your question and GF's answer, it looks like a computationally free, type- and range-safe implicit conversion from T(&)[20] to some_array_template<T,20> would be semantically possible, but boost::array doesn't allow that. Still, you might consider moving entirely to boost::array if you have lots of similar logic. And it's simple enough to use as a basis to roll your own, if you do want implicit conversion.
You are trying to use templates (compile time) to know how many items will be in your array at runtime. It is not possible to do like you try to do it.
You will have to rely on code outside your function or inside the function
There isn't such a construct in C++, that allows you to receive a compile time error.
But I think that using templates you could implement a framework containing something like a LimitedArray class to be used instead of a basic array type.