I'm just getting started with Object Oriented Programming. I'm trying to access member function of two different classes within a template function. I have restricted access to member functions based on boolean flag isAggregateElement. For some reason, Compiler throws error stating that there is no such member function.
class descriptor{
public:
int getName(){
return -5;
}
};
class aggregate{
public:
int getDescription() {
return 234;
}
int getUnit(){
return 1;
}
};
template <typename T>
void buildObjectInfo(const T& classMemberType, const bool& isDataInterface){
T baseTypeElement = classMemberType;
bool isAggregateElement = !isDataInterface;
if(isAggregateElement){
cout<<baseTypeElement.getUnit()<<endl;
} else {
cout<<baseTypeElement.getName()<<endl; // Error gets resolved if I remove the else construct
}
}
int main()
{
aggregate a;
descriptor d;
buildObjectInfo<aggregate>(a,false);
return 0;
}
What should I do to access getUnit() without deleting boolean condition (or) removing else construct in the template function ?
Both branches must be valid. Suppose you call buildObjectInfo(d,false), what should happen then?
You can use constexpr if to discard the false branch.
Note that the getters should be const methods. The template argument can be deduced from the function parameter and you do not need the bool:
#include <iostream>
#include <type_traits>
struct descriptor{
int getName() const { return -5; }
};
struct aggregate{
int getDescription() const { return 234; }
int getUnit() const { return 1; }
};
template <typename T>
void buildObjectInfo(const T& t){
if constexpr(std::is_same_v<aggregate,T>) {
std::cout << t.getUnit() << '\n';
} else {
std::cout << t.getName() << '\n';
}
}
int main() {
aggregate a;
descriptor d;
buildObjectInfo(a);
buildObjectInfo(d);
}
However, for only 2 different types an overloaded function is much simpler:
#include <iostream>
struct descriptor{
int getName() const { return -5; }
};
struct aggregate{
int getDescription() const { return 234; }
int getUnit() const { return 1; }
};
void buildObjectInfo(const aggregate& t) {
std::cout << t.getUnit() << '\n';
}
void buildObjectInfo(const descriptor& t) {
std::cout << t.getName() << '\n';
}
int main() {
aggregate a;
descriptor d;
buildObjectInfo(a);
buildObjectInfo(d);
}
Related
I got to know that we can also pass template arguments to choose which function should execute. I found them good alternative to function pointers since function pointers has run time cost but template parameters does not. Also, template parameters can be made inline whereas function pointers are not.
Alright then, this is what I wrote to depict my understanding on it. I came close but missing some minor detail somewhere.
template<class T>
class String {
public:
T str;
String() { std::cout << "Ctor called" << std::endl; }
};
template<class T, class C>
int compare(const String<T> &str1,
const String<T> &str2) {
for (int i = 0; (i < str1.length()) && (i < str2.length()); ++i) {
if (C::eq(str1[i], str2[i])) {
return false;
}
}
return true;
}
template<class T>
class Cmp1 {
static int eq(T a, T b) { std::cout << "Cmp1 called" << std::endl; return a==b; }
};
template<class T>
class Cmp2 {
static int eq(T a, T b) { std::cout << "Cmp2 called" << std::endl; return a!=b; }
};
int main() {
String<std::string> s;
s.str = "Foo";
String<std::string> t;
t.str = "Foo";
compare<String<std::string>, Cmp1<std::string> >(s, t);
// compare(s, t);
}
Details of the code:
I have an class String, which take an parameter and create member function of that type.
I have an compare function, which takes two String& arguments. Comparison function is passed to it.
Cmp1 and Cmp2 are two compare functions.
compare<String<std::string>, Cmp1<std::string> >(s, t);
does not get compile here. I tried some other ways to call but in vain.
Looks like you want something like that:
#include <iostream>
#include <string>
template<class T>
class String {
public:
T str;
String() { std::cout << "Ctor called" << std::endl; }
};
template<class T, class C>
int compare(const String<T> &str1,
const String<T> &str2) {
for (int i = 0; (i < str1.str.length()) && (i < str2.str.length()); ++i) {
if (C::eq(str1.str[i], str2.str[i])) {
return false;
}
}
return true;
}
template<class T>
class Cmp1 {
public:
static int eq(T a, T b) { std::cout << "Cmp1 called" << std::endl; return a==b; }
};
template<class T>
class Cmp2 {
public:
static int eq(T a, T b) { std::cout << "Cmp2 called" << std::endl; return a!=b; }
};
int main() {
String<std::string> s;
s.str = "Foo";
String<std::string> t;
t.str = "Foo";
compare<std::string, Cmp1<char> >(s, t);
// compare(s, t);
}
code
Explanations:
You already have String in definition of compare, you need to just send T which is std::string in your case.
You are trying to go through entire std::string, in compare, so, now your code compiles.
You calling cmp on str[index], that is actually char, so you need to call cmp with char template argument.
I am trying to understand different topics in C++ by examples and I cannot get this example to work:
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
operator T() const { std::cout << "In operator T()\n"; return val; }
};
int main()
{
const zero_init<int> x;
x(); //Error!
return 0;
}
I am obviously trying to call the operator() but it gives the error: "call of an object of a class type without appropriate operator()"
You accidentally implemented a type conversion operator and not operator(). Overload operator() like this instead (I removed the return value because you discard it in main anyway):
#include <iostream>
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
void operator()() const { std::cout << "In operator()\n"; }
};
int main()
{
const zero_init<int> x;
x();
return 0;
}
If you actually need the return value, do it like this:
#include <iostream>
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
T operator()() const { std::cout << "In operator()\n"; return val; }
};
int main()
{
const zero_init<int> x;
auto val = x();
return 0;
}
Suppose we need to instantiate a function that calls some class method from inside non-trivial code.
#include <iostream>
class A
{
public:
int f() { return 1; }
int g() { return 2; }
};
template <class T, int (T::*method)()>
int func(T& x)
{
// some complex code here calling method()
return (x.*method)();
}
int main()
{
A a;
std::cout << func<A, &A::f>(a) << "\n"
<< func<A, &A::g>(a) << "\n";
return 0;
}
This code compiles and works fine. Now suppose that the two methods are actually const and non-const, like this:
class A
{
int val_;
public:
A() : val_(0) {}
int alloc() { return ++val_; }
int get() const { return val_; }
};
This time we can't use the same approach, because the member functions have different signatures due to const qualifier. Moving the problem to run time does not seem to solve anything, Is there a way to avoid rewriting func() as two functions in this situation?
Can you change passing method from template parameter to function parameter?
If yes, this works:
#include <iostream>
class A
{
public:
int f() { return 1; }
int g() const { return 2; }
};
template <class T, class F>
int func(F method, T& x)
{
// some complex code here calling method()
return (x.*method)();
}
int main()
{
A a;
std::cout << func(&A::f, a) << "\n"
<< func(&A::g, a) << "\n";
return 0;
}
I wonder how to hide a real property field (not make it private or public but force to use setters and getters) and provide him with simple setter and getter. So I wonder how to create api like:
private:
Property( int my_a);
public:
Property( int my_b);
...
{
set_my_a(1);
cout << get_my_a() << endl;
// my_a = 13; // will cause compiler error
...
How to create such thing via Boost preprocessor?
Do you really need to use boost preprocessor?
you have a solution without boost below:
// property.h
#include <stdio.h>
#define property(type) struct : public Property <type>
template <typename T>
class Property
{
protected:
T value;
public:
virtual T get() {
return value;
}
virtual void set(T new_value) {
value = new_value;
}
};
usage example:
// test.cpp
#include "property.h"
class Test {
public:
property(int) {} a;
property(int) {
int get() {
return value * 10;
}
} b;
property(int) {
void set(int x) {
value = x * 200;
}
} c;
property(int) {
int get() {
return value * 3000;
}
void set(int x) {
value = x * 443;
}
} d;
};
main()
{
Test t;
printf("i\ta\tb\tc\td\t\n");
for (int i=0; i<10; i++) {
t.a.set(i);
t.b.set(i);
t.c.set(i);
t.d.set(i);
printf("%i\t%i\t%i\t%i\t%i\n", i, t.a.get(), t.b.get(), t.c.get(), t.d.get());
}
}
The wikipedia solution in http://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B is good but needs a minimal modification to become useful, because without the protected statement you cant write your own getters and setters.
#include <iostream>
template <typename T>
class property {
protected:
T value;
public:
T & operator = (const T &i) {
::std::cout << i << ::std::endl;
return value = i;
}
operator T const & () const {
return value;
}
};
struct Bar {
property <bool> alpha;
struct :public property <int> {
int & operator = (const int &i) {
::std::cout << "new setter " << i << ::std::endl;
return value = i;
}
} bravo;
};
main()
{
Bar b;
b.alpha = false;
b.bravo = (unsigned int) 1;
}
You can change a little more if you want:
#include <iostream>
#define SETTER(type) public: type& operator=(const type new_value)
#define GETTER(type) public: operator type const & () const
template <typename T>
class Property {
protected:
T value;
public:
T & operator = (const T &i) {
::std::cout << i << ::std::endl;
return value = i;
}
template <typename T2> T2 & operator = (const T2 &i) {
::std::cout << "T2: " << i << ::std::endl;
T2 &guard = value;
throw guard; // Never reached.
}
operator T const & () const {
return value;
}
};
struct Bar {
Property <bool> alpha;
struct:Property <int> {
SETTER(int) {
value = new_value * 1000;
::std::cout << "new method " << new_value << ::std::endl;
return value;
}
GETTER(int) {
return value/1000;
}
} bravo;
};
main()
{
Bar b;
b.alpha = false;
b.bravo = (unsigned int) 1;
::std::cout << b.bravo << ::std::endl;
}
Rather than rewrite an example of the implementation, this is the link for one on Wikipedia: http://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
This basically forces the property to be accessed through getter/setter methods. The upgrade you would need to get your desired effect is the ability to pass functors to these properties. There are plenty of ideas on implementing these; the best approach I cannot advise and depends on your developmental needs. Personally, it feels like over engineering and prefer to just use Pimpl to hide my private details and just provide the getters/setters explicitly.
The following code compiles (without warnings) on both clang++-2.9 and g++-4.6. However, the g++ binary Seg Faults, while the clang++ binary runs as intended.
What is the proper way to access template class data members through pointers when overloading []?
Here's the code:
#include <iostream>
template <typename T>
class A {
private:
T val1;
T val2;
public:
T& getVal1() { return val1; }
void setVal1(T aVal) { val1 = aVal; }
T& getVal2() { return val2; }
void setVal2(T aVal) { val2 = aVal; }
};
template <typename T>
class B {
private:
A<T>* aPtr;
public:
A<T>* getAPtr() { return aPtr; }
T& operator[](const int& key) {
if(key == 0) { T& res = getAPtr()->getVal1();
return res; }
else { T& res = getAPtr()->getVal2();
return res; }
}
};
int main()
{
B<int> foo;
foo[0] = 1;
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
You are returning a reference to a local variable (res). The reference won't be valid after returning from operator[]. It could be overwritten by other stuff. What really happens is Undefined: that is why compilers are allowed to eat your children or grow a moustache: Undefined Behaviour
You probably want to return by value.
Edit
Since you have a setter, you don't need the reference: See the solution live at http://ideone.com/oxslQ
Note: there was another problem with aPtr not being initialized. I proposed a simple constructor for that. _You might want to initialize this from elsewhere OR you need
assignment and copy constructors
or use a shared_ptr for aPtr
.
#include <iostream>
template <typename T>
class A
{
private:
T val1;
T val2;
public:
T getVal1()
{
return val1;
}
void setVal1(T aVal)
{
val1 = aVal;
}
T getVal2()
{
return val2;
}
void setVal2(T aVal)
{
val2 = aVal;
}
};
template <typename T>
class B
{
private:
A<T>* aPtr;
B(const B&); // TODO , disallow for now
B& operator=(const B&); // TODO , disallow for now
public:
B() : aPtr(new A<T>()) {}
~B() { delete aPtr; }
A<T>* getAPtr()
{
return aPtr;
}
T operator[](const int& key)
{
if(key == 0)
{
T res = getAPtr()->getVal1();
return res;
}
else
{
T res = getAPtr()->getVal2();
return res;
}
}
};
int main()
{
B<int> foo;
foo.getAPtr()->setVal1(1);
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
If you want to return by ref, then your A::getValX() functions should also return by ref, and your res variable inside B::operator should also be T& instead of T:
#include <iostream>
template <typename T>
class A {
private:
T val1;
T val2;
public:
T& getVal1() { return val1; }
void setVal1(T aVal) { val1 = aVal; }
T& getVal2() { return val2; }
void setVal2(T aVal) { val2 = aVal; }
};
template <typename T>
class B {
private:
A<T>* aPtr;
public:
A<T>* getAPtr() { return aPtr; }
T& operator[](const int& key) {
if(key == 0) { T& res = getAPtr()->getVal1();
return res; }
else { T& res = getAPtr()->getVal2();
return res; }
}
};
int main()
{
B<int> foo;
foo[0] = 1;
int x = foo[0];
std::cout << foo[0] << " " << x << std::endl; // 1 1
}
(Note that it will still crash at runtime, since aPtr isn't initialized anywhere.)
Your original code returns a reference to the local variable res, not to A::val1 / A::val2 as you probably intended. If res is a non-reference variable, then it will be a simple copy of the val1 / val2 value, that is only valid for inside the scope (in this case the function) where it was declared. So you need a reference here.