I'm using a third party C++ library which has a json parsing class that has
different functions like this:
GetInt
GetBool
GetString
GetDouble
I'd like to write a utility function that can access this class. I'm thinking of something like this:
class <template T>
class MyClass {
static T getValue(ThirdPartyClass someObj, const string &key) {
if(someObj[key].IsDouble())
return someObj[key].GetDouble();
else if (someObj[key].IsString())
return someObj[key].GetString();
// ... (for other types)
}
}
The caller of this class will hold the correct return type.
However this is ugly. Is there any way (using macro substitution for example) I can avoid the if conditions? The third party class has IsXXTypeXX and corresponding GetXXTypeXX functions (where XXTypeXX is Int, Double,String or Bool).
I know the return type when I call the function for eg:
int i = getValue(someObj, "intKey");
string s = getValue(someObj, "strKey");
So I dont need the if conditions at all. Ideally I would look to have something so
I would be able to do this:
int i = MyClass<int>::getValue(someObj, "intKey");
string s = MyClass<string>::getValue(someObj, "strKey");
Why not just write a bunch of static Get functions (GetInt/GetDouble...) that validates the input, returns the appropriate type result and throws an exception if it isn't that type?
Technically you can achieve the public interface that you've outlined there but that would involve very ugly looking template specialization.
It would probably be better if you just had a bunch of static functions instead. Here is what template specialization would look like:
template <typename T> class MyClass {
static T getValue(ThirdPartyClass someObj, const string &key) {
// handle types that you didn't specialize for
}
};
template <> class MyClass <string> {
static string getValue(ThirdPartyClass someObj, const string &key) {
return someObj[key].GetString();
}
};
template <> class MyClass <int> {
static int getValue(ThirdPartyClass someObj, const string &key) {
return someObj[key].GetInt();
}
};
//..
Skeleton key for software engineering: add an intermediate layer.
#include <string>
#include <cassert>
using std::string;
class Proxy {
public:
enum Type {
Int,
Bool,
String,
Double
};
Type type;
int i;
bool b;
string s;
double d;
operator int() const {
assert(type == Int);
return i;
}
operator bool() const {
assert(type == Bool);
return b;
}
operator string() const {
assert(type == String);
return s;
}
operator double() const {
assert(type == Double);
return d;
}
Proxy(int i) : type(Int), i(i) {}
Proxy(bool b) : type(Bool), b(b) {}
Proxy(string s) : type(String), s(s) {}
Proxy(double d) : type(Double), d(d) {}
}; // class Proxy
Proxy getValue(ThirdPartyClass someObj, const string &key) {
if (someObj[key].IsDouble())
return someObj[key].GetDouble();
else if (someObj[key].IsString())
return someObj[key].GetString();
//... (for other types)
}
int main() {
int i = getValue(someObj, "intKey"); // if type does not match, a exception will be thrown.
string s = getValue(someObj, "strKey");
}
The code you showed won't compile. You can't in the same function return a double, a string, and an int. What you'd have to do it specialize for each return type, and then call only the function for that type:
template <>
class MyClass<int> getValue(ThirdPartyClass someObj, const string& key) {
if(someOjb[key].IsInt()) return someObj[key].GetInt();
else { /* Maybe throw an exception */ }
};
and repeat for each type.
Now, you're probably thinking, "this is silly, how come I have to specialize each type?" That's because your JSON library is using type erasure, so you have to check the type at runtime. The only way to save yourself the work is if the library provides a templated get.
If you want, you could create a macro to stamp these instantiations out. It would take advantage of the # (stringification) and ## (concatenation) features of the preprocessor. It'll probably be clearer to just write them out.
Related
I have a class that reads a script and stores variables and their respective types which I reference by name (string). The class has a templatized member method named get, which fetches the variable that matches the name parameter from one of the member tables, and needs to return data of the same type as the variable type it is assigned to. To better illustrate my question, suppose I have a simplified class such as:
class Script
{
public:
template <typename T>
T get(std::string name);
private:
//data stored in associative containers such as:
// std::map<std::string, int> int_data;
// std::map<std::string, bool> bool_data;
};
In the implementation file, the member method is specialized:
template <>
int Script::get(std::string name)
{
int value = retrieve_integer(name);
return value;
}
template <>
bool Script::get(std::string name)
{
bool value = retrieve_boolean(name);
return value;
}
I would like to call the class method like this without explicitly providing the type with script.get<int>("varName"):
Script script; // loads script and reads data
bool useVsync = script.get("useVsync"); // calls the bool specific get
int screenWidth = script.get("screenWidth"); // calls the int specific get
How would I do this without causing could not deduce template parameter T errors? I know that this should be possible, because I have used libraries that allowed such syntax.
As explained in comment, you may abuse of conversion operator:
class Script
{
public:
struct ValueProxy {
operator bool() const {
return script.bool_data[name];
}
operator int() const {
return script.int_data[name];
}
ValueProxy& operator= (bool b) {script.bool_data[name] = b;}
ValueProxy& operator= (int n) {script.int_data[name] = n;}
Script &script;
std::string name;
};
ValueProxy get(std::string name) {
return ValueProxy{*this, std::move(name)};
}
private:
//data stored in associative containers such as:
std::map<std::string, int> int_data;
std::map<std::string, bool> bool_data;
};
int main() {
Script script;
script.get("myBool") = true;
script.get("MyInt") = 42;
bool b = script.get("MyBool"); // true
int i = script.get("MyInt"); // 42
int j = script.get("MyBool"); // 0
}
I have an algorithm (not preseted here) which takes as input different parameters (int, float, vectors).
My idea of design was to have an container which holds all these differents parameters.
To achive this, I have a base class Parameter and a derivated template class TypeParameter.
These parameters will be holded in a container.
The design is presented below:
#pragma once
#include <utility>
#include <memory>
#include <string>
#include <vector>
namespace parameter
{
/*
Interface for parameter
*/
class Parameter
{
public:
Parameter() {}
Parameter(std::string param_name) : name(param_name) {}
Parameter(const Parameter&& other) noexcept : name(std::move(other.name)) {}
virtual ~Parameter() {}
inline const std::string get_name() { return name; }
private:
std::string name;
};
/*
*/
template<class T>
class TypeParameter
: public Parameter
{
public:
TypeParameter(std::string param_name, T new_value) : Parameter(param_name), value(new_value) {}
TypeParameter(const TypeParameter&& other) noexcept : Parameter(std::move(other)), value(std::move(other.T)) {}
inline const T get_value() { return value; }
private:
T value;
};
/*
Container for parameters
*/
class ParameterSet
{
public:
ParameterSet() {}
void add(std::unique_ptr<Parameter> param) { data.push_back(std::move(param)); }
private:
std::vector <std::unique_ptr<Parameter>> data;
};
} //namespace parameter
The main is:
#include <iostream>
#include <string>
#include "Parameter.h"
using parameter::TypeParameter;
using parameter::Parameter;
using parameter::ParameterSet;
void foo(std::unique_ptr<Parameter> p)
{
std::cout << p->get_value(); // ERROR
}
int main(int argc, char *argv[])
{
TypeParameter<int> *iparam = new TypeParameter<int>("ee", 3);
std::unique_ptr<Parameter> p = std::make_unique <TypeParameter<int>>("foo", 3);
foo(std::move(p));
ParameterSet param_set;
param_set.add(std::unique_ptr<Parameter>(iparam));
param_set.add(std::move(p));
getchar();
}
My problem is I cannot get the value without a cast.
Hence, my question is how do I cast the unique_ptr from a Parameter class to derived TypeParameter.
Is there another way to design the container?
Thanks a lot!
You don't have to reinvent the wheel. There are a couple of classes you can use from the standard library:
std::variant.
As suggested by the comments, variant is a type-safe union of a pre-defined set of data types, which you put in the templates argument of variant.
For example, a std::variant<int,float,double> can hold any value of type int, float, or double, but nothing else.
To use the stored value, you can either use the visitor pattern with the std::visit() function. Other functions allow you to know which of the preset types is stored in the variable (index()) and to extract the value from it (using get()). If you try to extract the value of the wrong type, the get() function throws an exception
std::any
is another utility that can hold different data types. As opposed to variant, you don't have to know the types at compile-time. Basically, it stores a void* to the data with a typeinfo to remember its original type. You can then use any_cast to cast the variable back to its original type. Just like variant, an exception is thrown when trying to cast to the wrong type.
These two classes are available in C++ 17. If these features are not available to you, they were also included in boost (respectively boost:variant and boost:any)
You can store the set of values in a standard library container, e.g. in a std::vector<std::variant<int,float,double>> or a std::vector<std::any>>.
Alternative to std::variant/std::any is the old way polymorphism:
class Parameter
{
public:
Parameter(const std::string& param_name) : name(param_name) {}
virtual ~Parameter() = default;
const std::string& get_name() const { return name; }
virtual void printValue() const = 0;
// Other virtual methods
private:
std::string name;
};
template<class T>
class TypeParameter : public Parameter
{
public:
TypeParameter(const std::string& name, const T& t) : Parameter(name), value(t) {}
// Non virtual method when we don't access it by base class.
const T& get_value() const { return value; }
void printValue() const { std::cout << value; }
private:
T value;
};
And then your
void foo(const Parameter& p)
{
std::cout << p.get_value(); // ERROR
}
becomes
void foo(const Parameter& p)
{
p.print();
}
If you don't want to add many virtual methods to Parameter, then Visitor pattern can help, but then you have to know each derived types.
I don't know what to do. I always get an error by using a simple class and a simple template function. I read all the other solutions but they didn't helped me.
Next to some other classes I have the simple class Data:
class Data{
public:
template <class T>
T dat();
int id;
union{
char c;
int i;
double d;
};
};
and the function dat:
template <class T>
T Data::dat(){
if(id == 1) return i;
if(id == 2) return d;
if(id == 3) return c;
}
As you can see, I want to check the id and return int, double or char.
Now I've tried to print the value in the main function like this:
Data test;
test.id=1;
test.i = 12;
cout<<test.dat();
But I always get this error message:
Error: Could not find a match for Data::dat<Data::T>() needed in main(int, char**).
Where is the problem??
Thank you
To put it precisely, you want the return type of the function to
depend on it's the id field in the object; in other words,
dynamically. Templates are resolved at compile time, so they
cannot help here. You'll have to return something like
boost::variant or boost::any, which supports such dynamic
typing.
Use this:
cout<<test.dat<int>();
dat() has no parameters involving T, so the compiler cannot deduce T from the call and it must be provided explicitly, e.g.:
cout << test.dat<int>();
Also, bear in mind you must implement dat() in the header file.
I don't know what to do. I always get an error by using a simple class and a simple template function. I read all the other solutions but they didn't helped me.
It seems to me that you want to create a discriminated union.
Your implementation won't work, because the return type of a template function is determined at compilation time (i.e. before you set a value in id and try to call the function.
Solution:
class Data
{
enum value_type {
int_val, char_val, double_val
} discriminator; // private (the user doesn't see this)
// this replaces your id
union{
char c;
int i;
double d;
} value;
public:
class wrong_type_error: public std::logic_error
{
public:
wrong_type_error(const std::string& msg): std::logic_error(msg) {}
};
explicit Data(const char c)
: discriminator(Data::char_value)
, value.c(c)
{
}
explicit Data(const int i)
: discriminator(Data::int_value)
, value.i(i)
{
}
explicit Data(const double d)
: discriminator(Data::double_value)
, value.d(d)
{
}
// don't put this here: int id;
// this part can be optimized to simpler (more idiomatic) code
template<typename T> T get() const; // undefined
template<> int get() const {
if(discriminator != Data::int_val)
throw wrong_type_error("Cannot return a int from Data instance");
return value.i;
}
template<> char get() const {
if(discriminator != Data::char_val)
throw wrong_type_error("Cannot return a char from Data instance");
return value.c;
}
template<> double get() const {
if(discriminator != Data::double_val)
throw wrong_type_error("Cannot return a double from Data instance");
return value.d;
}
};
Client code:
Data test(10.5);
cout<<test.get<double>();
All that said, you should consider using a boost::variant or boost::any instance, depending on your needs.
VS2012 says
"error C2783: 'T Data::dat(void)' : could not deduce template argument for 'T'"
You just need to tell the function dat what T is:
cout<<test.dat<int>();
The template type can be deduced if you pass a templated parameter, but it cannmot guess the return type.
I am trying to make a class that can have functions and members controlled by a template argument. I am thinking of something like this.
template<int control>
class ControlledDeclaration
{
public:
if(1 == control)
int Get() { return 0; }
else if(2 == control)
char* Get() { return "get"; }
else if (3 == control)
bool Get() { return true; }
};
void test_template()
{
ControlledDeclaration<1> c_int;
ControlledDeclaration<2> tx_int;
ControlledDeclaration<3> b_int;
}
If possible, how to do it?
The approach I would use is along the lines of specializing the details in a traits class and provide the interface using the template. In this trivial example there isn't much benefit of using traits rather than specializing the actual type but in general customizing the few points of variations is easier using traits than specializations.
template <int> struct ControlDeclarationTraits;
template <>
struct ControlDeclarationTraits<1> {
typedef int type;
static int value() { return 0; };
};
template <>
struct ControlDeclarationTraits<2> {
typedef char const* type;
static char const* value() { return "get"; }
};
template <>
struct ControlDeclarationTraits<3> {
typedef bool type;
static bool value() { return true; }
};
template<int control>
class ControlledDeclaration
{
public:
typename ControlDeclarationTraits<control>::type Get() {
return ControlDeclarationTraits<control>::value();
}
};
BTW, the type of string literals is char const[n] (for a suitable n) and not char[n], i.e., you can't really use a string literal to initialize a char*. It does work because it was deemed necessary to support existing code to assign string literals to char* but it is actually a lie: trying to assign a value to any of the values causes undefined behavior. Making the pointer const makes it obvious that the content isn't meant to be modified.
Have a look at boost::enable_if, that does exactly what you want.
There is some class which have methods like:
int getSomething1();
std::string getSomething2();
someClass getSomething3();
There is structure which describes fields of this class like:
{"name of field", pointer to getter, std::type_info}
Then I would like to use it as follows:
if(type == int){
field_int = (int)getter();
}
else if(type == std::string){
field_string = (std::string)getter();
}
etc.
How to transform getters like
int getSomething1();
std::string getSomething2();
etc.
to some universal function pointer and then to get the correct value of field?
This answer of mine to another question addresses your problem pretty well. With some minor modifications, you get this:
template<class C, class T>
T get_attribute(const C& instance, T (C::*func)() const) {
return (instance.*func)();
}
Assuming the following:
struct Foo {
int getSomething1() const;
std::string getSomething2() const;
someClass getSomething3() const;
};
You can use it like this:
Foo foo;
int value = get_attribute<Foo, int>(foo, &Foo::getSomething1);
std::string value = get_attribute<Foo, std::string>(foo, &Foo::getSomething2);
someClass value = get_attribute<Foo, someClass>(foo, &Foo::getSomething3);
You can of course transform get_attribute to a functor to bind some or all of the arguments.
There is no formal universal function pointer, the equivalent of void*
for data. The usual solution is to use void (*)(); you are guaranteed
that you can convert any (non-member) function pointer to this (or any
other function pointer type) and back without loss of information.
If there is a certain similarity in the function signatures (e.g. all
are getters, with no arguments) and how they are used, it may be
possible to handle this with an abstract base class and a set of derived
classes (possibly templated); putting pointers to instances of these
classes in a map would definitely be more elegant than an enormous
switch.
What you are trying to achieve can be better achieved with already existing containers such as a boost fusion sequence. I'd advice that you try this first.
Templates to the rescue!
// Create mapping of type to specific function
template <typename T> T getSomething(); // No default implementation
template <> int getSomething<int>() { return getSomething1(); }
template <> std::string getSomething<std::string>() { return getSomething2(); }
template <> someClass getSomething<someClass>() { return getSomething3(); }
// Convenience wrapper
template <typename T> void getSomething(T& t) { t = getSomething<T>(); }
// Use
int i = getSomething<int>();
std::string s;
getSomething(s);
As I understand, your difficulty is in storing the function pointers, since they are of different types. You can solve this using Boost.Any and Boost.Function.
#include <boost/any.hpp>
#include <boost/function.hpp>
int getInt() {
return 0;
}
std::string getString() {
return "hello";
}
int main()
{
boost::function<boost::any ()> intFunc(getInt);
boost::function<boost::any ()> strFunc(getString);
int i = boost::any_cast<int>(intFunc());
std::string str = boost::any_cast<std::string>(strFunc());
}