'if' statements on C++ template arguments - c++

The following code gives me a warning when using the Intel compiler icpc13.
#include <iostream>
template<int N>
class base
{
public:
double x[N];
};
template<int N>
class derived : public base<2*N>
{
public:
void print()
{
if (N==1)
{
std::cout << this->x[1] << std::endl;
}
else if (N==2)
{
std::cout << this->x[3] << std::endl;
}
}
};
int main(int argc, char* argv[])
{
derived<1> temp1;
derived<2> temp2;
temp1.print();
temp2.print();
}
Result: % icpc-13.1.163 main.cpp main.cpp(29): warning #175:
subscript out of range std::cout<x[3]<
during instantiation of "void derived::print() [with N=1]" at line
41
This is obviously not a danger since the if statement protects this line of code if the template argument is 1.
I know that I "should" do template specialization for such things, but there is some shared code in the real functions that make the if statements on template arguments really handy.
Question is…is this a "bad" thing to do, or is this incorrect compiler behavior? I don't get warnings with gcc, or xlc.
I chose the pragma solution. This ends up looking like this:
void print()
{
#ifdef __INTEL_COMPILER
#pragma warning push
#pragma warning disable 175
#endif
if (N==1)
{
std::cout<<this->x[1]<<std::endl;
}
else if (N==2)
{
std::cout << this->x[3] << std::endl;
}
#ifdef __INTEL_COMPILER
#pragma warning pop
#endif
}
};
From what I can tell, the push saves the warning flags before the disable, and the pop restores them.

Compilers do check code branches even they're inactive due to compile-time constants.
ICPC seems to be the only that checks the array bounds, but you could possibly run into other annoying warnings, like from Visual C++, which warns about constant conditional expressions when checking N (C4127 with warning level 4).
I definitely would take care of the warning, your mileage may vary:
disable warning with compiler option -wd175
disable warning at this specific point with vendor-dependent #pragma warning(disable: 175)
specialize the derived<>::print() method and factor out common code:
template<>
void derived<1>::print()
{
std::cout << x[1] << std::endl;
call_common();
}
template<>
void derived<2>::print()
{
std::cout << x[3] << std::endl;
call_common();
}
factor out the switch on N into its own specialized method derived<>::print_x():
template<>
void derived<1>::print_x()
{
std::cout << x[1] << std::endl;
}
template<>
void derived<2>::print_x()
{
std::cout << x[3] << std::endl;
}
template<int N>
void derived<N>::print()
{
print_x();
// ...
}

Related

Is there any way to "capture" local scope template variables (akin to a lisp special) and use as default?

In the following code:
#include <iostream>
#include <type_traits>
// this code below won't compile unless at this point there is already
// a typename rate
// using rate = std::integral_constant<int, 10>;
template<typename rate=rate> void functionA()
{
static_assert(rate::value > 5);
std::cout << "functionA: " << rate::value << std::endl;
}
template<typename rate> void loop()
{
functionA<std::integral_constant<int, 50>>(); // => prints "functionA: 50"
functionA(); // <- I would like this call to infer loop's functions's rate template type (which would be 20)
}
void other()
{
using rate = std::integral_constant<int, 12>;
functionA(); // => prints "functionA: 12"
}
void bare_loop()
{
functionA(); // should give compile time-error: no 'rate' in semantic scope
}
int main() {
loop<std::integral_constant<int, 20>>();
return 0;
}
I would like to be able to write a templated function which has a default parameter value which remains undeclared until compilation is necessary.
The above shows the most minimal piece of code to express this idea.
What I'm trying to achieve is directly inspired by Lisp's variable capture mechanism (lexical and dynamic scope).
To be clear: this is entirely a compile-time problem and solution.
Is this possible in the current state of affaires of C++?
I am really not sure if I understood what you intended, but if you want is a function that could either take the parameter rate at compile time or at runtime, well, one possibility would be to use a default parameter in function "functionA", determined by the template parameter. So you can define the default in compile time, and override it in runtime if needed.
I leave an example below in case it helps, however I can't understand what it the desired use case, and it can be possibly
a bad design:
#include <iostream>
#include <cassert>
constexpr int infer() { return 123; }
const int MIN_RATE_LIM = 5;
template<int t_rate=infer()> void functionA(const int rate = t_rate)
{
assert(rate > MIN_RATE_LIM);
std::cout << "functionA: " << rate << std::endl;
}
template<int rate> void loop()
{
for(int i = 0; i < 10; i++ )
{
functionA<50>();
functionA<rate>();
functionA<rate>(12); // <- 12 will overlap the value 10 in this line
}
}
int main() {
loop<10>();
return 0;
}

Collect instantiated template types at compile time

Consider this snippet:
struct A {
template <typename T> void bar(const T &) {
/*
I would like to write something like:
if constexpr(type T already processed/T is already present in typelist)
...do something fancy
else if constexpr(type T not processed/T is not present in typelist)
*/
}
};
struct Msg1 {};
struct Msg2 {};
int main() {
A a;
a.bar(Msg1{});
a.bar(Msg1{});
}
Demo
Is it possible to see at compile time for which types the method bar was already instantiated?
Ideally, there would be some kind of growing typelist, where one can check at compile time for which types bar is already instantiated.
No. It is not possible to do so at compile time. However, it would be possible to do the following at runtime:
#include <typeindex>
#include <type_traits>
#include <unordered_set>
struct A {
private:
std::unordered_set<std::type_index> already_processed_ts;
public:
template<typename T>
void bar(const T&){
if(already_processed_ts.find(typeid(T)) != already_processed_ts.end())
std::cout << "Already processed " << typeid(T).name() << std::endl;
else{
already_processed_ts.insert(typeid(T));
std::cout << "Processing " << typeid(T).name() << "... \n";
}
}
}
struct Msg{};
int main()
{
f(Msg{}); // Prints "Processing 3Msg..." (at least on my compiler)
f(Msg{}); // Prints "Already processed 3Msg"
return 0;
}

Using CRTP To Deterministically Generate Code

I've been recently getting into template wizardry and in particular CRTP. I know that templates are used to make the compiler generate code for us so I was wondering if it were possible to make a template "decide" which parts of a function we would like it to include for a particular class. For example if I have the following code:
crtp.h
#include <iostream>
using std::endl;
using std::cout;
template<class T>
class A {
public:
void func() {
constexpr unsigned short mask = T::GetMask();
if (mask & 1) {
/*
Do Something
*/
cout << "Mask 1" << endl;
}
if (mask & 1 << 3) {
/*
Do Something else
*/
cout << "Mask 2" << endl;
}
}
};
class B : public A<B> {
friend class A<B>;
protected:
static constexpr unsigned short GetMask() { return 0x0001; }
};
class C : public A<C> {
friend class A<C>;
protected:
static constexpr unsigned short GetMask() { return 0x0009; }
};
main.cpp
#include "ctrp.h"
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
using std::getchar;
using std::endl;
int main() {
B b;
C c;
cout << "B:" << endl;
b.func();
cout << endl << "C:" << endl;
c.func();
getchar();
}
Which when executed produces:
B:
Mask 1
C:
Mask 1
Mask 2
This works great, does exactly what I want it to. The problem is from my standpoint the if statements should be unnecessary. As I am dealing with constant expressions the compiler should have everything it needs to simply skip the branching and know to execute the first part for class B and both parts for class C.
I would like to cash in on this and specifically tell the compiler to remove the sections that are unnecessary for the particular class to avoid unnecessary branching at runtime. Unfortunately I have no idea how to do this, any ideas? Thanks in advance
Edit
In response to some of the awesome suggestions C++17's constexpr if expression is a near perfect solution that I had no idea existed, but am unfortunately unable to use. I am limited to using C++14.
If you care about performance, the compiler will very likely optimize out all "dead" branches and even the if condition, if it can evaluate it during compile time.
What is worse, all the branches need to be well formed until C++17 constexpr if. In this case, you can "outsource" the functionality to special (static member) functions and use specialization to invoke the right one. See #R Sahu's answer for the example.
Emulating if/else at compile time using template metaprogramming does not work that way. You have to imagine if/else using a different mindset.
Instead of
if (mask & 1) {
/*
Do Something
*/
cout << "Mask 1" << endl;
}
if (mask & 1 << 3) {
/*
Do Something else
*/
cout << "Mask 2" << endl;
}
you'll have to use something along the lines of:
function1_selector<mask & 1>::dostuff();
function2_selector<mask & 1 << 3 >::dostuff();
where
template <bool> struct function1_selector
{
static void dostuff() { /* Do nothing */ }
};
template <> struct function1_selector<true> // Specialize for true
{
static void dostuff() { /* Do something useful */ }
};
Add code for function2_selector similarly.

Is there a way to warn about unused data members of a struct?

There are situations where all the members of a struct are expected to be used in a function.
Often times, more data gets added to the struct, but it does not get handled in the corresponding function. This leads to unused data, that we would like to be warned about. Is there some kind of compiler attribute that can do this?
Here is an example, of what I was hoping it would look like:
#include <iostream>
struct Foo{
int a=0;
int b=0;
int c=0;
};
void handle_all_data(Foo f __attribute__((use_all)) );
int main(){
Foo f;
handle_all_data(f);
}
void handle_all_data(Foo f){
std::cout << f.a << '\n';
std::cout << f.b << '\n';
}
//warning: f.c is not used anywhere in "handle_all_data"
Is there some way to do this?

Separate Compilation of template with non-type argument

I'm trying to do separate compilation of my non-type argument template, but I am facing a few problems. My application has a number of opcodes, and each opcode is associated with certain data and functions, many of which are similar and could be defined in the primary template, then the differences can be put into specialized templates.
So here is the original in-line code:
(header)
#include <iostream>
template<int opcode>
class BAR
{
public:
BAR(){};
void foo()
{
std::cout << "BAR<int>::foo()" << std::endl;
}
};
// specialize 1 and 2, otherwise we go with the
// primary definition.
template <>
void BAR<1>::foo()
{
std::cout << "BAR<1>::foo()" << std::endl;
}
template <>
void BAR<2>::foo()
{
std::cout << "BAR<2>::foo()" << std::endl;
}
I have a simple 'main':
int main(int argc, char* argv[])
{
BAR<1> bar_1;
BAR<2> bar_2;
BAR<3> bar_3;
bar_1.foo();
bar_2.foo();
bar_3.foo();
return 0;
}
I managed to put the specializations for '1' and '2' into a cpp file and this is
what I have:
(header)
#include <iostream>
template<int opcode>
class BAR
{
public:
BAR(){};
void foo()
{
std::cout << "BAR<int>::foo()" << std::endl;
}
};
// specialize 1 and 2, otherwise we go with the
// primary definition.
template<> void BAR<1>::foo() ;
template<> void BAR<2>::foo() ;
(cpp)
#include "Foo.h"
#include <iostream>
template<> void BAR<1>::foo()
{
std::cout << "BAR<1>::foo()" << std::endl;
}
template<> void BAR<2>::foo()
{
std::cout << "BAR<2>::foo()" << std::endl;
}
void x()
{
BAR<1> b1;
BAR<2> b2;
b1.foo();
b2.foo();
}
I not real crazy about the x() function, but without this I get unresolved symbols for BAR<1>::foo() & BAR<2>::foo(), if there's a better way, I'd be interested ( MSVC 13 compiler).
Ok so far so good. I really want to put the primary definition of the function foo() into the CPP, but I can't seem to get the syntax right:
template<> void BAR<int>::foo()
{
std::cout << "BAR<int>::foo()" << std::endl;
}
this is not allowed by the compiler, and rightly so I guess, is not
a valid non-type value. So what is the magic words to use to do this?
Many thanks in advance for any help.
dave.