C++ Property template

B

Brent Ritchie

Hello,

This is my first attempt at template programming so please bear with me.
This is what I have so far:

Property.h --------------------------

#ifndef PROPERTY_H_
#define PROPERTY_H_

enum Access{ READ, WRITE, FULL };

template<typename T, Access type = FULL>
class Property
{
public:
Property(const T& val, T (*getter)(T) = defaultGet, T (*setter)(T,
T) = defaultSet ): value(val) { Get = getter; Set = setter; }
virtual operator T() { return Get(value); }
virtual T operator=( T& val ) { return Set(value, val); }
protected:
T value;
T (*Get) (T);
T (*Set) (T, T);
static T defaultGet(T val) { return val; }
static T defaultSet(T dest, T source) { dest = source; }
};

#endif


and here is my test program:

main.cpp ---------------------------------

#include <cstdlib>
#include <iostream>

#include "Property.h"

static int anotherGetter(int value) { return 10; }

int main(int argc, char *argv[])
{
Property<int> t(1);
std::cout << t << std::endl;
t = 5;
std::cout << t << std::endl;
t = 2;
std::cout << t << std::endl;
// Up to here everything works fine but:
Property<int> f(1, anotherGetter);
std::cout << f << std::endl; // This returns 10 like it's supposed to
f = 5;
std::cout << f << std::endl;
// But this returns 5 not 10
f = 2;
std::cout << f << std::endl;
// And again this returns 2 not 10.
system("pause");
}

My question is why does it work the way I intended the first time but not
the second or third time?

Any help would be appreciated
 
G

\/Gogineni\/

t=5,t=2,f=5,f=2 calls your copy constructor (your constructor is used
as a copy constructor)
 
D

Dave Rahardja

Hello,

This is my first attempt at template programming so please bear with me.
This is what I have so far:

Property.h --------------------------

#ifndef PROPERTY_H_
#define PROPERTY_H_

enum Access{ READ, WRITE, FULL };

template<typename T, Access type = FULL>
class Property
{
public:
Property(const T& val, T (*getter)(T) = defaultGet, T (*setter)(T,
T) = defaultSet ): value(val) { Get = getter; Set = setter; }
virtual operator T() { return Get(value); }
virtual T operator=( T& val ) { return Set(value, val); }
protected:
T value;
T (*Get) (T);
T (*Set) (T, T);
static T defaultGet(T val) { return val; }
static T defaultSet(T dest, T source) { dest = source; }
};

#endif


and here is my test program:

main.cpp ---------------------------------

#include <cstdlib>
#include <iostream>

#include "Property.h"

static int anotherGetter(int value) { return 10; }

int main(int argc, char *argv[])
{
Property<int> t(1);
std::cout << t << std::endl;
t = 5;
std::cout << t << std::endl;
t = 2;
std::cout << t << std::endl;
// Up to here everything works fine but:
Property<int> f(1, anotherGetter);
std::cout << f << std::endl; // This returns 10 like it's supposed to
f = 5;
std::cout << f << std::endl;
// But this returns 5 not 10
f = 2;
std::cout << f << std::endl;
// And again this returns 2 not 10.
system("pause");
}

My question is why does it work the way I intended the first time but not
the second or third time?

Any help would be appreciated

Your assignment

f = 5;

is inadvertently (to you) interpreted as

f = Property<int>(5);

instead of

f.operator=(5);

In other words, a temporary Property<int> object is created, which is then
assigned to f.

The reason for this behavior is that your assignment operator takes a
non-const T&. Make sure that your assignment operators are in the form:

T& operator=(const T&);

A constructor that can be called with a single argument is also likely to
cause this kind of automatic temporary object creation. Perhaps you should
declare your constructor explicit.
 
M

Matthias Kaeppler

Brent said:
Property<int> f(1, anotherGetter);
std::cout << f << std::endl; // This returns 10 like it's supposed to
f = 5;
std::cout << f << std::endl;
// But this returns 5 not 10

I guess the problem is this:
The expression 'f = 5' creates a new Property object using the copy
constructor and assigns it to 'f'. Since you are setting the default
getter in the copy constructor, it returns 'value' and not '10', so the
output is '5'.

Try making your copy constructor explicit, then it must not be used for
type conversion. Another issue might be that AFAICT you operator= has a
wrong signature: 'val' should be of type reference-to-const T, not
reference-to-T.

Hope that helps,
Matthias
 
M

Matthias Kaeppler

Matthias said:
Try making your copy constructor explicit, then it must not be used for
type conversion.

One more note about copy constructors:

Although your copy constructor has /three/ formal parameters, it can be
called by the client only taking /one/ argument, since you're assigning
default values to parameters two and three.
Since every copy constructor which can be called using one argument
qualifies for implicit type conversion, you should make it 'explicit',
unless you want type conversion to happen (this form of conversion can
be very useful, but it's also error prone because it's hard to
detect--as you probably noted yourself now).

One more C++ subtlety which pops up now and then :)

Regards,
Matthias
 
B

Brent Ritchie

Thanks all,

This is what I have so far for my C++ Property Template:

Property.h-------------------------

#ifndef PROPERTY_H_
#define PROPERTY_H_

enum Access{ READ, WRITE, FULL };

template<typename T, Access type = FULL>
class Property
{
public:
explicit Property( const T& val,
const T (*getter)(const T&) = defaultGet,
const T (*setter)(T&, const T&) = defaultSet )
: value(val)
{
if (getter == NULL)
{
Get = defaultGet;
}
else { Get = getter; }
if ( setter == NULL )
{
Set = defaultSet;
}
else
{
Set = setter;
}
}

explicit Property( Property *val )
{
val.value = this->value;
val.Get = this->Get;
val.Set = this->Set;
}

virtual operator T()
{
return Get(value);
}
virtual T operator=( const T& val )
{
return Set(value, val);
}

protected:
const T (*Get) ( const T& );
const T (*Set) ( T&, const T& );
T value;

static const T defaultGet(const T& val)
{
return val;
}

static const T defaultSet(T& dest, const T& source)
{
dest = source;
return dest;
}

};

#endif

main.cpp-----------------------------

#include <cstdlib>
#include <iostream>

#include "../Property.h"

static const int anotherGetter(const int& value) { return value * -1; }
static const int anotherSetter(int& value, const int& value2 ) { value +=
value2; return value; }

int main(int argc, char *argv[])
{
int i = 0;

Property<int> a(2);
std::cout << "Property<int> a(2)" << std::endl;
std::cout << "Input value to test default set functionality: ";
std::cin >> i;
a = i;
std::cout << "Testing default get functionality: ";
std::cout << a << std::endl << std::endl;

Property<int> b(2, anotherGetter);
std::cout << "Property<int> b(2, anotherGetter)" << std::endl;
std::cout << "Input value to test default set functionality: ";
std::cin >> i;
b = i;
std::cout << "Testing anotherGetter functionality: ";
std::cout << b << std::endl << std::endl;

Property<int> c(2, NULL, anotherSetter);
std::cout << "Property<int> c(2, NULL, anotherSetter)" << std::endl;
std::cout << "Input value to test anotherSetter functionality: ";
std::cin >> i;
c = i;
std::cout << "Testing default get functionality: ";
std::cout << c << std::endl << std::endl;

Property<int> d(2, anotherGetter, anotherSetter);
std::cout << "Property<int> d(2, anotherGetter, anotherSetter)" <<
std::endl;
std::cout << "Input value to test anotherSetter functionality: ";
std::cin >> i;
d = i;
std::cout << "Testing anotherGetter functionality: ";
std::cout << d << std::endl << std::endl;

Property<int> x = d;
std::cout << x << std::endl;
x = 2;
std::cout << x << std::endl;
x = 1;
std::cout << x << std::endl;
system("pause");

return 0;

}

Next, I'm going to add the overloaded versions of this template for
read-only and write-only access. Then I'm going to start testing all
primitives not just int. Then I have to start testing user defined classes.
<shutters>

Any comments on what I have so far? Anything you see as potentially
broken? Feature changes/requests ;) ? Anyone think this might be potentially
useful to anyone but me?

Also if you notice this compiles nicely with no warnings even with -Wall
turned on. I think thats a first for me!

Anyways, Thanks for all the help. This template programming isn't all
that different from regular programming but it has it's nuances though.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top