I wrote a C++11 function that conditionally initializes an object. Consider the following example:
arma::mat some_function(bool a, bool b, unsigned long int c) {
if(a) {
if(b) {
arma::sp_mat d(c, c);
} else {
arma::SpMat<short> d(c, c);
}
} else {
if(b) {
arma::mat d(c, c, fill::zeros);
} else {
arma::Mat<short> d(c, c, fill::zeros);
}
}
// Some other computations and a return statement
}
As you can see, I want to initialize an object d
where the (Armadillo) data type depends on booleans a
and b
. Unfortunately, it does not compile, with the compiler complaining about d
not being declared in the scope where it is subsequently used.
I found various discussions on similar issues. Conditional initialization is apparently introduced in C++17. However, I need to use C++11 in this application. Other suggestions mention the type_traits
library and lambda functions. And many discussions focus on conditionally specifying the object's content rather than its data type. As I am still fairly new to C++, I am not sure how to apply any of these options to the this nested conditional structure.
template<class F>
arma::mat some_function(bool a, bool b, unsigned long int c, F f) {
if(a) {
if(b) {
arma::sp_mat d(c, c);
return f(d);
}
arma::SpMat<short> d(c, c);
return f(d);
}
if(b) {
arma::mat d(c, c, fill::zeros);
return f(d);
}
arma::Mat<short> d(c, c, fill::zeros);
return f(d);
}
arma::mat some_function(bool a, bool b, unsigned long int c){
return some_function(a, b, c,
[](auto&& d)->arma::mat{
// Some other computations and a return statement
}
);
}
I believe this solves your problem, but requires c++14.
The style here is called "continuation passing style"; the first template creates the variable d
, then passes that to a function you passed to it. It then returns what the function it takes returns when it in turn takes a d
.
The code within the lambda at the bottom must be able to handle d
being any of the types it could be within the template function, because it doesn't know what the bool values will be.
(If the bool values are known there are other approaches, but often the answer is "if you know that, and only some are valid, why do the bools exist?)
The c++14 feature was so simple to write most c++11 compilers support it if you tell it to use c++1y (their early pre-standard version of C++14). If not, you can just replace this:
[](auto&& d)->arma::mat{
// Some other computations and a return statement
}
with
struct some_helper {
template<class M>
arma::mat operator()(M&& m)const {
// Some other computations and a return statement
}
};
outside of some_function
, then
arma::mat some_function(bool a, bool b, unsigned long int c){
return some_function(a, b, c,
some_helper{}
);
}
In the c++14 case, if you need access to a/b/c, change []
to [&]
.
In the c++11 case, if you need access to a/b/c, add them to some_helper
as member variables (or references) and initialize them in some_helper
's constructor, and pass them in from some_function
.
The c++14 feature I'm using is the terse syntax templated lambda, which is syntactic sugar to generate the trivial c++11 class. The c++14 lambda has a few more (zero cost) features we don't care about.