Amit's receivable class

M

markspace

Hi all,

I saw a great idea the other day. You can read about it here:
http://amitp.blogspot.com/2004/04/c-references-vs-pointers.html

Basically, Amit has this receivable class (template, actually) that
allows you to mark which function parameters are being modified, for
easier reading.

His class is defined here:

template<typename T>
struct receivable {
explicit receivable(T& receiver): receiver_(receiver) {}
T& get() { return receiver_; }
operator T& () { return receiver_; }
T& operator = (const T& v) { receiver_ = v; return receiver_; }
private:
T& receiver_;
};

and he gives the following example of it's use:

void f(receivable<int> i) {
i = 5;
}

int main() {
int j;
f(j); // this line causes a compile-time error
f(receive(j)); // this line works
}

Ok, so far so good. *But*, the example does't compile, because the word
"receive" isn't defined. (Not because of the line that says "this line
causes a compile-time error", yes I was smart enough to comment that out
first. ;) ) So I'm confused, and I thought I'd ask here about it.

What I got to work is "f(receivable<int>(j));" This runs correctly, and
is only a bit more wordy. I'm not very good at reading class definitions
though, and determining all the ways to use that class, so I have a few
questions.

First, is there a better way to convert j to a receivable class?

What, for example does "operator T& () { return receiver_; }" do? Is
that a type conversion or cast? How the heck do I use it?

What the heck is get()? Is this just for converting a receivable to
it's base(?) type? Like:
void f(receivable<int> i) {
i = 5;
std::cout << i.get() << std::endl;
}

Lastly, operator= seems clear enough. But the const bit confuses me.
Is there a case where the right hand expression of an assignment would
*not* be const? It seems like there couldn't be...


And here's my own little observation: The receivable class is similar
to the practice of #define OUT and IN to be nothing, allowing a
programmer to mark parameters as changable or not:

#define OUT
#define IN
void f(OUT int& i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT j, IN 3); // cheesy, OUT does nothing
}

But if one renames the receivable class to OUT, you can do this:

void f(OUT<int> i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT<int>(j), IN 3); // better...
}

And it's less typing too. :)
 
R

red floyd

markspace said:
Hi all,

I saw a great idea the other day. You can read about it here:
http://amitp.blogspot.com/2004/04/c-references-vs-pointers.html

Basically, Amit has this receivable class (template, actually) that
allows you to mark which function parameters are being modified, for
easier reading.

His class is defined here:

template<typename T>
struct receivable {
explicit receivable(T& receiver): receiver_(receiver) {}
T& get() { return receiver_; }
operator T& () { return receiver_; }
T& operator = (const T& v) { receiver_ = v; return receiver_; }
private:
T& receiver_;
};

and he gives the following example of it's use:

void f(receivable<int> i) {
i = 5;
}

int main() {
int j;
f(j); // this line causes a compile-time error
f(receive(j)); // this line works
}

Ok, so far so good. *But*, the example does't compile, because the word
"receive" isn't defined. (Not because of the line that says "this line
causes a compile-time error", yes I was smart enough to comment that out
first. ;) ) So I'm confused, and I thought I'd ask here about it.

What I got to work is "f(receivable<int>(j));" This runs correctly, and
is only a bit more wordy. I'm not very good at reading class definitions
though, and determining all the ways to use that class, so I have a few
questions.

First, is there a better way to convert j to a receivable class?

What, for example does "operator T& () { return receiver_; }" do? Is
that a type conversion or cast? How the heck do I use it?

What the heck is get()? Is this just for converting a receivable to
it's base(?) type? Like:
void f(receivable<int> i) {
i = 5;
std::cout << i.get() << std::endl;
}

Lastly, operator= seems clear enough. But the const bit confuses me. Is
there a case where the right hand expression of an assignment would
*not* be const? It seems like there couldn't be...


And here's my own little observation: The receivable class is similar
to the practice of #define OUT and IN to be nothing, allowing a
programmer to mark parameters as changable or not:

#define OUT
#define IN
void f(OUT int& i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT j, IN 3); // cheesy, OUT does nothing
}

But if one renames the receivable class to OUT, you can do this:

void f(OUT<int> i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT<int>(j), IN 3); // better...
}

And it's less typing too. :)

Try this:

template<class T>
inline receivable<T>(T& val)
{
return receivable<T>(val);
}

Sort of like the std::make_pair<>() function
 
J

John Harrison

markspace said:
Hi all,

I saw a great idea the other day. You can read about it here:
http://amitp.blogspot.com/2004/04/c-references-vs-pointers.html

Basically, Amit has this receivable class (template, actually) that
allows you to mark which function parameters are being modified, for
easier reading.

His class is defined here:

template<typename T>
struct receivable {
explicit receivable(T& receiver): receiver_(receiver) {}
T& get() { return receiver_; }
operator T& () { return receiver_; }
T& operator = (const T& v) { receiver_ = v; return receiver_; }
private:
T& receiver_;
};

and he gives the following example of it's use:

void f(receivable<int> i) {
i = 5;
}

int main() {
int j;
f(j); // this line causes a compile-time error
f(receive(j)); // this line works
}

Ok, so far so good. *But*, the example does't compile, because the word
"receive" isn't defined. (Not because of the line that says "this line
causes a compile-time error", yes I was smart enough to comment that out
first. ;) ) So I'm confused, and I thought I'd ask here about it.

I would imagine that Amit forgot to add this convenience function

template <typename T>
inline receivable<T> receive(T& t)
{
return receivable<T>(t);
}

This is a common practise with template classes, you add a function which
makes objects of the class. This way you get the benefits of template
function argument type deduction. Look at make_pair in the standard library
for instance.

What I got to work is "f(receivable<int>(j));" This runs correctly, and
is only a bit more wordy. I'm not very good at reading class definitions
though, and determining all the ways to use that class, so I have a few
questions.

First, is there a better way to convert j to a receivable class?

See above.
What, for example does "operator T& () { return receiver_; }" do? Is
that a type conversion or cast? How the heck do I use it?

Yes its a type conversion operator. It should be called implicitly in most
situations, but if it isn't you can call it explcitly with a static_cast.

int x = 0;
reciever<int> r(x);
int y = r; // implicit conversion
double z = static_cast said:
What the heck is get()? Is this just for converting a receivable to
it's base(?) type? Like:
void f(receivable<int> i) {
i = 5;
std::cout << i.get() << std::endl;
}

I guess, but note that get returns a non-const reference.
Lastly, operator= seems clear enough. But the const bit confuses me.
Is there a case where the right hand expression of an assignment would
*not* be const? It seems like there couldn't be...

It's very rare, but std::auto_ptr is one example which uses this. When you
assign from an auto_ptr the auto_ptr itself changes its value.
And here's my own little observation: The receivable class is similar
to the practice of #define OUT and IN to be nothing, allowing a
programmer to mark parameters as changable or not:

#define OUT
#define IN
void f(OUT int& i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT j, IN 3); // cheesy, OUT does nothing
}

But if one renames the receivable class to OUT, you can do this:

void f(OUT<int> i, IN int k) {
i = k;
}

int main() {
int j;
f(OUT<int>(j), IN 3); // better...
}

And it's less typing too. :)

john
 
R

red floyd

John said:
I would imagine that Amit forgot to add this convenience function

template <typename T>
inline receivable<T> receive(T& t)
{
return receivable<T>(t);
}

Thanks, John. I had the same thing, but I messed up the syntax (typing off the
top of my head).
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top