I have a class that has a constructor taking quite a few parameters
enum class FooType {FOO_A, FOO_B, FOO_C};
class Foo {
Foo(const double a, const double b, .... const double n);
}
depending on the 'type', I only need a certain subset of the params. At the moment there are various constructors with different number of inputs, but some new types will be added so that the number of inputs is the same. I could just add the type to the constructor, have a long switch in it, but the params list is quite long.
Foo(FooType type, const double a, const double b, .... const double n) {
if (type = FooType::FOO_A) {
...
} else if ....
}
Doesn't seem too bad, but I also don't like having that long parameter list. Seems to easy to make typos that are a pain to debug. So I can
a.) pass a structure in
b.) do something else
and I am just curious about potential b solutions.
Is it possible to templateize this such that I could create a template constructor and call the constructor with something like
std::make_shared<Foo<FooType::FOO_A>>(a, b, c);
Note: I don't want to use inheritance since the rest of the class' functionality has absolutely no use/need for it.
This could be a use case for the named parameters idiom: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-parameter-idiom.html .
That would allow your constructor call to look like this:
File f = OpenFile("foo.txt")
.readonly()
.createIfNotExist()
.appendWhenWriting()
.blockSize(1024)
.unbuffered()
.exclusiveAccess();
Instead of the above example, you could have a helper class that contains all the named parameters and your class constructor would take an instance of the parameters class as its parameters.
This lets you freely pick the set of parameters that you initialize at construction time. If you want to enforce different subsets being initialized for the different types, then you should just write different constructor versions.
Here is how you could make a templated constructor using a builder pattern:
class Foo {
double a;
int b;
double c;
public:
Foo(double a, int b, char c) {
}
};
template <FooType Type>
class Builder { };
template <>
class Builder<FooType::FOO_A> {
double _a;
public:
Builder& a(double val) { _a = val; return *this; }
Foo build() { return { _a, 0, 0 }; }
};
template <>
class Builder<FooType::FOO_B> {
int _b;
public:
Builder& b(int val) { _b = val; return *this; }
Foo build() { return { 0.0, _b, 0 }; }
};
template <>
class Builder<FooType::FOO_C> {
char _c;
public:
Builder& c(char val) { _c = val; return *this; }
Foo build() { return { 0.0, 0, _c }; }
};
The Builder class is templated as you wanted and the code you were executing in that if else statement, you can execute in builder's constructor or the build function on the instance of Foo you will be returning.
In the example a is relevant for FOO_A, b is relevant for FOO_B and c for FOO_C and other values get initialized to their default value.
This is how you would use it:
int main() {
Foo testA = Builder<FooType::FOO_A>().a(12.5).build();
Foo testB = Builder<FooType::FOO_B>().b(10).build();
Foo testC = Builder<FooType::FOO_C>().c('x').build();
return 0;
}
It is a pretty small example for a builder pattern, but from your example it seems like you are using much more arguments. To add another argument to any of the builder specializations in form Builder& typeName(Type val) { _typeName = val; return *this; } (it should return self reference so these funtions can be chained).
Related
Consider the following classes
class A {
public:
virtual std::string to_string() = 0;
};
class B : public A {
public:
std::string to_string(){
return "B";
}
};
class C : public A {
public:
std::string to_string(){
return "C";
}
};
class D {};
Now I will have a variant std::variant<B*, C*, D*> bcd;The variable can of course, depending on the user input, hold either a variable of type B* or of type C*. At a given time in the program, I want to extract the value to pass to a method taking the superclass A* as an argument. Now, if I do this explicitly like this:
bc = new C();
A* a = std::get<C*>(bc);
It works as expected. But I do not know at this point, which type the inner value of bc will have.
I have tried the following:
Adding A* to the variant and trying to access it with std::get<A*>(bc), which results in a bad variant access.
Creating a visitor pattern to return the type (even though this seems cumbersome for this simple task) like this
class Visitor {
template<typename TType>
TType operator()(TType arg) {
return arg;
}
};
A* a2 = std::visit(visitor, bc);
which produces a no type named ‘type’ in ‘std::conditional_t‘. (I also tried it without templates).
Is there a right / elegant way to do this without having to do something like
if(B* b = std::get_if<B*>(bc))
for every type that I have?
You were close with std::visit, not sure what the error is exactly but I would recommend using a lambda instead of a custom struct:
int main(){
std::variant<B*, C*> bc;
bc = new C();
A* a = std::visit([](auto&& e)->A*{return e;},bc);
a->to_string();
}
The above will compile iff all variants can be casted to A* and is thus safe.
If some of variants are not derived from A*, you could use this longer version:
A* a = std::visit(
[](auto& e) -> A* {
if constexpr (std::is_convertible_v<decltype(e),A*>)
return e;
else
return nullptr;
},
bc);
It will return nullptr if the currently held type cannot be casted to A.
One can hide the ugly lambda (the simple one above can be hidden too) into a global variable template, full example:
template <typename T>
constexpr auto shared_cast = [](auto& e) -> T* {
if constexpr (std::is_convertible_v<decltype(e), T*>)
return e;
else
return nullptr;
};
int main() {
std::variant<B*, C*, D*> bc;
bc = new C();
A* a = std::visit(shared_cast<A>, bc);
if (a != nullptr) a->to_string();
}
Feel free to refactor the whole std::visit expression into a template function:
template <typename T,typename V>
T* visit2(V& variant){
return std::visit(shared_cast<T>,variant);
}
If I do something like
mixin template T() {
float y;
this(float y_){
y = y_;
}
}
class C {
mixin T!() t;
int x;
this(int x_, float y_){
x = x_;
this(y_);
}
}
//
C c = new C(5, 7.0f);
This gives the error
constructor C.this (int x_, float y_) is not callable using argument types (float). On the line that contains this(y_); in C's constructor, which seems to imply that C can't see the constructor imported from T. Though, it should.
Obviously t.this(...) and T!().this(...) don't work.
The most obvious workaround I can think of is like (a proxy):
template T(...) {
void constructor(...){...}
this(Args...)(Args args){ constructor(args); }
}
...
class C {
mixin T!() t;
this(...){
t.constructor(...);
}
}
But that sucks because it puts more knowledge overhead on T (using the constructor requires doing something special)
Is there any way to call t's constructor in a non-weird (and non-special-case) way? Also, is this a bug? If not, why does it work this way?
The issue stems from the fact that things mixed into an aggregate via mixin templates are not inserted into the aggregate's overload set.
For normal methods the way to get around that is to use an alias into the scope introduced by the template mixin like so:
mixin template T()
{
void foo()
{
}
}
class C
{
mixin T!() t;
alias foo = t.foo;
void foo(int _)
{
foo();
}
}
However, for constructors, the analogue doesn't work and it's a reported bug:
alias this = t.this; // Won't compile
alias __ctor = t.__ctor // Using the hidden __ctor name won't compile, either
If you don't need to call the mixed in constructor from outside, you can call it via the builtin name:
mixin template T()
{
float y;
this(float y_)
{
y = y_;
}
}
class C
{
mixin T!() t;
int x;
this(int x_, float y_)
{
x = x_;
t.__ctor(y_); // This
}
}
Working on a command line parser for myself. I knew immediately that I was going to have trouble with this construct and hoping someone could provide suggestions for a work around.
I want to store the argument list of parameters (based off a template) in a vector that will possibly contain a variety of different data types. But from my understanding, you have to define the vector<template<type>> statically. Is there a way to except multiple types?
Here is an example of what I mean:
#include <vector>
#include <memory>
namespace clparser
{
class CommandLine {
private:
std::vector<Parameter<AnyType??>*> ArgumentList;
public:
void Add(Parameter<AnyType??>* Parameter) { ArgumentList.push_back(Parameter); }
};
template<typename T>
class Parameter {
private:
const char *ShortOption;
const char *LongOption;
const char *Description;
const bool RequiredField;
const char *DefaultValue;
public:
Parameter(const char *ShortOption, const char *LongOption, const char *Description, const bool RequiredField, const char *DefaultValue)
: ShortOption(ShortOption), LongOption(LongOption), Description(Description), RequiredField(RequiredField), DefaultValue(DefaultValue)
{
}
};
}
If you can accept a C++11 solution, I propose you a iper-simplified version from my command line parser. Hoping that can be of inspiration for you.
The idea behind my solution is the use of base/derived polymorphism: a pure virtual class optBase that is a base for a set of template classes dedicated to options (in the following example, only class opt; but there are other three in my parser).
Then the (not template) class yData contain a std::unique_ptr<optBase> (if you use a simple pointer to optBase you can compile in C++98 too, I suppose; but I suggest the use of C++11 or newer).
class yData correspond (roughly) to your tou your class Parameter but (here is the trick) isn't a template class; contain a base pointer to a template class.
My class yarg correspond to your class clparser and my std::map<int, yData> idMap correspond (roughly) to your std::vector<Parameter<AnyType??>*> ArgumentList.
To feed idMap, I've developed a set of template method (one for every derived from optbase classes); in the following example you can see a iper-semplified version of one of them: addOpt() (corresponding, roughly, to your Add()).
In the following example you can see a little main() with a couple of uses for addOpt(): the first for a int parameter and the second for a double parameter (important (and weak point of my solution): the returned value must be saved in a reference variable, not in a simple variable).
#include <map>
#include <memory>
class optBase
{
public:
// some pure virtual methods
};
template <typename X>
class opt : public optBase
{
private:
X val { };
// ...
public:
opt ()
{ }
opt (X const & v0)
: val { v0 } // ...
{ }
X const & getVal () const
{ return val; }
X & getVal ()
{ return val; }
// ...
};
// other optBase derived classes (for flags, containers of values, etc)
class yData
{
private:
// ...
std::unique_ptr<optBase> optB;
public:
yData (/* other arguments */ std::unique_ptr<optBase> optB0)
: /* something else */ optB { std::move(optB0) }
{ }
// ...
std::unique_ptr<optBase> const & getPnt () const
{ return optB; }
};
class yarg
{
private:
// ...
std::map<int, yData> idMap;
// ...
public:
// ...
template <typename T>
T & addOpt (/* other arguments */ T const & def = T())
{
int id { /* some value */ };
opt<T> * optP { nullptr };
// ...&
idMap.emplace(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(/* other arguments */
std::unique_ptr<optBase>(optP = new opt<T>(def))));
return optP->getVal();
}
};
int main ()
{
yarg y;
// important: use a *reference*
auto & pi = y.addOpt(3); // pi is a int
auto & pd = y.addOpt(3.0); // pd is a double
static_assert(std::is_same<decltype(pi), int &>::value, "!");
static_assert(std::is_same<decltype(pd), double &>::value, "!!");
}
I wrote a C++ class that parses expressions like "2 * SQRT(5) + 1". I've created a class called c_function that "represents" the usual mathematical functions like sqrt, sin, cos etc. something like as follows:
class c_function {
std::string name;
double (*function)(double);
public:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
function = ffunction;
}
// ...
};
Then I have a different class that contains a std::vector of these c_function objects:
class expression {
std::vector<c_function> functions;
// etc...
public:
// ctor:
expression(/* ... */) {
// ...
functions.push_back(c_function("SQRT", sqrt));
functions.push_back(c_function("SIN" , sin));
functions.push_back(c_function("COS" , cos));
// ...
}
};
The point is that all these functions have one argument. That is fine for most cases but I want to enable adding custom functions to the expression class and also want to support custom functions with more that one argument (for example, to define a function AREA(a, b) that returns the product of the two values a and b).
I did that by adding an argument counter argumentCount and more function "properties" to the c_function class:
class c_function {
std::string name;
unsigned int argumentCount;
double (*function1)(double);
double (*function2)(double, double);
// ...
};
and used two constructors:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
argumentCount = 1;
function1 = ffunction;
function2 = NULL;
};
c_function(std::string fname, double (*ffunction)(double, double)) {
name = fname;
argumentCount = 2;
function1 = NULL;
function2 = ffunction;
};
and added the methods to the expression class:
// add custom function with one argument
void addFunction(std::string fname, double (*ffunction)(double));
// add custom function with two arguments
void addFunction(std::string fname, double (*ffunction)(double, double));
so that one can define
double Expression_Area(double width, double height) {
return (width * height);
}
and introduce it to the expression class with
myExpression.addFunction("AREA", Expression_Area);
That works fine and this way I can also add more function "properties" and functions constructors allowing any number of arguments, but
there is always a limit of the number of arguments that is supported
the code becomes ugly by having multiple constructors, methods to add a function, and code within the interpretation of the expression just because the number of arguments may be different.
I wonder if there is a way to support functions with any number of arguments more general. I tried changing the c_function class to:
class c_function {
std::string name;
unsigned int argumentCount;
double (*function)(...);
// ...
};
but this does not work because functions with fixed number of arguments are not accepted by (...).
Is there any way to get this solved with one constructor, one function "property" etc.?
C++11 onwards
In C++11 you can use a variadic template to declare a class that will take a function with a variable number of arguments:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
template <typename... Args>
class Foo {
public:
Foo (const std::string& name, double (*func)(Args...))
: name_{name}, func_{func} {}
double call(Args... args) {
return func_(args...);
}
// Thanks to W.F. for reminding me of the operator() overload
// This does the same thing as a call to the "call" method.
double operator()(Args... args) {
return func_(args...);
}
private:
std::string name_;
double (*func_)(Args...);
};
int main() {
Foo<double> t1("test1", bar);
Foo<double, double> t2("test2", bar2);
// NOTE: in C++17 you can declare Foo without the template
// arguments: they will be deduced.
// Foo t1("test1", bar); // C++17
// Foo t2("test2", bar2); // C++17
std::cout << t1.call(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
You can tweak this basic solution as to how you need your classes to work.
C++98
Pre C++11, you will probably be forced to create classes that take functions with a different number of arguments, so your classes would look something like this:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
class Foo1 {
public:
// let's typedef the function pointer for two reasons:
// 1. readability
// 2. duplicating the class for a different number of arguments
// means we need to do less modifications to the code because
// we can catch a few changes in one line here.
typedef double (*Function)(double);
Foo1 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x) {
return func_(x);
}
private:
std::string name_;
Function func_;
};
class Foo2 {
public:
typedef double (*Function)(double, double);
Foo2 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x, double y) {
return func_(x, y);
}
private:
std::string name_;
Function func_;
};
// etc. for classes Foo3, Foo4, ... up until you think you will
// need no more.
int main() {
Foo1 t1("test1", bar);
Foo2 t2("test2", bar2);
std::cout << t1(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
There's a bit of code duplication here, but it's not too bad.
Remarks
Finally, (although I did not show it above because I think it goes without saying) wherever there is code duplication, template the types so that you can reduce this as much as possible. So, for example, you might consider modifying the C++11 Foo class above to:
template <typename T, typename... Args>
class Foo {
public:
typedef T (*Function)(Args...);
Foo (const std::string& name, const Function func)
: name_{name}, func_{func} {}
T operator()(Args... args) {
return func_(args);
}
private:
std::string name_;
Function func_;
};
I am building a static library that will be used on many future projects. I do not want to limit the interface of a particular function in this static library so the application codes can have flexibility in data types. This library will hold pure virtual base class pointers to objects the user will need.
At first, I tried templating this function. However, I would have to template the pure virtual base function as well (In the code shown, BBInterface::Go) - apparently this is not possible.
The visitor pattern sounded like it might be applicable, but I'm afraid I just don't get it. I furthermore don't understand if I can keep the static library black-boxed, or if the static library would have to be re-compiled and linked with a new set of "visitors" anytime someone adds a possible data type.
I am now trying to create a templated struct inside the function, which is then reinterpret_cast-ed to a (hopefully?) equivalent struct. See below
This seems to work for two inputs (A and B). This is ok, but I'd ideally want to use variadic templates to have potentially many inputs. This is over my head at this point. If anyone could help with that, it'd be great.
So, is there a more elegant way to keep an extensible function interface (BBContainer::Do in the code below)? Is there a way to avoid reinterpret_cast? Can I extend this to more than two templated arguments? Is there a way to check for success of reinterpret_cast like dynamic_cast?
#include <iostream>
#include <vector>
#include <map>
#include <memory>
using namespace std;
static const double values[] = {0., 1., 2., 3., 4., 5., 6. };
// ------ ASSUME THIS BLACK BOX AREA IS IN A STATIC LIBRARY THE USER CAN NOT MODIFY -------//
struct BBPacket {};
class BBInterface
{
public:
virtual void Go(BBPacket&) = 0;
};
class BBContainer
{
public:
void Add(const string aName, std::unique_ptr<BBInterface>&& aThing)
{
BBMap[aName] = std::move(aThing);
}
template <typename A, typename B>
void Do(const std::string& aName, A& aVal, const B& aIndex)
{
struct NewPacket : public BBPacket
{
NewPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {}
A& mVal;
const B& mIndex;
};
NewPacket temp(aVal, aIndex);
this->Do(aName, temp);
}
void Do(const string& aName, BBPacket& aPacket)
{
BBMap[aName]->Go(aPacket);
}
private:
map<std::string, unique_ptr<BBInterface>> BBMap;
};
// ----- The user area is written by the user, and should not be included in the blackbox project! ---------
struct USingleValuePacket
{
double& mVal;
const int& mIndex;
};
struct UVectorValuePacket
{
vector<double>& mVals;
const vector<int>& mIndices;
};
class USingleExtractor : public BBInterface
{
virtual void Go(BBPacket& aPacket)
{
USingleValuePacket& danger = reinterpret_cast<USingleValuePacket&>(aPacket);
fprintf(stdout, "The current single value is %1.1f\n", danger.mVal);
danger.mVal = values[danger.mIndex];
}
};
class UVectorExtractor : public BBInterface
{
virtual void Go(BBPacket& aPacket)
{
UVectorValuePacket& danger = reinterpret_cast<UVectorValuePacket&>(aPacket);
for (int i = 0; i < danger.mVals.size(); ++i)
{
fprintf(stdout, "The current vector value %i is %1.1f\n",i, danger.mVals[i]);
danger.mVals[i] = values[danger.mIndices[i]];
}
}
};
int main()
{
BBContainer a;
a.Add("f", std::unique_ptr<USingleExtractor>(new USingleExtractor));
a.Add("g", std::unique_ptr<UVectorExtractor>(new UVectorExtractor));
double val = 0.;
int index = 4;
a.Do("f", val, index);
fprintf(stdout, "Expected value is 4.0 and I get %1.1f\n", val);
std::vector<double> valVec(3);
std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5);
a.Do("g", valVec, indexVec);
fprintf(stdout, "Expected value for index 0 is 0.0 and I get %1.1f\n", valVec[0]);
fprintf(stdout, "Expected value for index 1 is 2.0 and I get %1.1f\n", valVec[1]);
fprintf(stdout, "Expected value for index 2 is 5.0 and I get %1.1f\n", valVec[2]);
// a.Do("g", val, index); // This will go into UVectorExtractor with USingleValuePacket data - Not compatible!
return 0;
}
EDIT:
I want BBContainer::Do to have a flexible signature (in this example I use (string, double&, const int&) and (string, vector&, const vector&), but I may have many more). At the same time I do not want to modify BBInterface (for example with Go(double&, const int), Go(vector&, const vector&), and so on). I can assume the derived class of BBInterface knows what data its particular implementation of Go requires. So how do I forward generic data from BBContainer::Do to the derived classes of BBInterface when it only has access to the BBInterface base class - which is not allowed to be specialized? And, is there a more type-safe method than generating a templated struct in the BBInterface base class and using reinterpret_cast in its derived classes?
As Hurkyl pointed out, I should just make a packet. It seems good enough with that and a helper function to keep the interface of Do clean and a dynamic_cast instead of reinterpret_cast. I'm still working on variadic templates for variable length packets.
New packet:
template<typename A, typename B>
struct UPacket : public BBPacket
{
UPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {}
A& mVal;
const B& mIndex;
};
Helper function:
template <typename A, typename B>
void Do(BBContainer& a, const string& aName, A& aVal, const B& aIndex)
{
a.Do(aName, UPacket<A, B>(aVal, aIndex));
}
Usage:
...
double val = 0.;
int index = 4;
Do(a,"f", val, index);
...
std::vector<double> valVec(3);
std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5);
Do(a, "g", valVec, indexVec);