C++ conventions

I’m still transitioning to C++: sometimes I want my generic “utility” functions to do slightly different things, and I’m curious about conventions in C++ for these different things.

mapcar() replacement
If I have a collection (vector, queue, etc.) of things, and I want to do the same thing to a bunch of them, I’ll just write a function that iterates through them and does that thing. In LISP there’s mapcar() (or in Scheme map()). In Java, there’s no straightforward analogue. In C++, should I write a mapcar() function (really: template function) that takes a function pointer as an argument? Do people do this very often?

modifying an object vs. returning a new one
Sometimes I want a function to modify the object I pass (by reference) to it. Other times I want it to return an entirely new object that’s been modified. Is there a way of distinguishing these types of functions with the function name, or is it better for utility functions to modify the object directly and assume you’ll make a duplicate beforehand if you really want one?

passing pointers vs. call by reference
Obviously it makes sense to use call by reference when you have local objects you’ll be calling the function with, and pointers when you have persistent objects you’ll be passing to the function. But sometimes you have just as many of each. Then do you use to call by reference, and dereference pointers before passing them, or pass pointers and reference/add an ampersand to objects before passing them?

If any of these are unclear I’d be glad to provide some code examples.

Thank you my gurus!

yo Kyle,

i started a reply to this but ended up getting distracted and it got lost…

mapcar()

this sort of problem is usually solved at the data-structure level. for example, if i have a whole bunch of particles, i’d have a Particles class that contains a collection of Particle object. then i’d write a function on the Particles class to do it. so i can just call eg Particles::Render() which will then render all the particles.

you could use a template function if you wanted to, though. just be careful about how maintainable or un-maintainable it makes your code: templated stuff is ugly, and will be more difficult for others to understand.

modifying vs returning

the way this is supposed to be solved is using the const keyword. by declaring a pass-by-reference argument to a function as const, you are saying that this reference will not be modified by your function. but in practise, this can be difficult. it is not possible to convert a const reference to a non-const reference, so a reference declared as const cannot have non-const methods called on it, nor can it be passed as a non-const argument to any other functions.

in any case, you should probably only ever return a pointer: if you return a reference, your reference is to memory on the stack rather than the heap, which becomes invalid as soon as the function where the reference was constructed returns. following this convention, i can probably safely assume that a function which takes a non-const reference to an object will modify that object, while a function that returns a pointer to an object will create a new object.

not that if you just return a non-reference non-pointer object, you’re returning-by-copy, which suffers from the same problems associated with pass-by-copy.

personally, when it comes to things like this i like to make it very clear in the variable/function name and comments what is going on. for example:

  
/// Fills the given teapot with tea  
void FillTeapot( Teapot& teapot_to_fill );  
  
vs  
/// creates a new tea-filled teapot from source_teapot  
Teapot* CreateFilledTeapot( const Teapot& source_teapot );  
  
or, if doing return-by-copy,  
/// creates a new tea-filled teapot from source_teapot  
Teapot CreateFilledTeapot( const Teapot& source_teapot );  
  

passing pointers vs. call by reference

do whatever makes the code easier to understand. internally, pass-by-reference and pass-by-pointer compile to the same assembly code. in function declarations/signatures, it’s perhaps a good idea to avoid mixing pass-by-pointer and pass-by-reference for non-const references: either use all pointers, or all non-const references, but not both.

the above applies more to generating function declarations, of course. if you’re just asking about what to do when calling a function, it matters less i think. when i’m looking at other people’s code i don’t pay so much attention to the little * or & marks - i pay a lot more attention to the variable name. only if i really need to do i look closely at whether something is a reference or a pointer or whatever. but if the variable names are good i shouldn’t need to do this. if it seems unclear, try renaming the variables, or even adding extra intermediary variables to clear up exactly what’s going on.

hope this helps
d

it does. even if I didn’t ask.

thanks!

j

@ modifying vs returning
const correctness is quite a vast topic. The parashift FAQs are usually a good resource for issues like that:

http://www.parashift.com/c+±faq-lite/c-…-tness.html

@ passing pointers vs. call by reference
http://www.parashift.com/c+±faq-lite/r-…-ml#faq-8.6

There’s a lot of really useful info here – thanks everybody. I’ve run into the parashift FAQs many times but never saw the pointers vs. references one. I feel like this is a great maxim:

Use references when you can, and pointers when you have to.

Damian, why do you say “templated stuff is ugly”? Personally, I find Java generics to be quite beautiful, and templates to be a vague approximation that shares many merits.

Damian, why do you say “templated stuff is ugly”? Personally, I find Java generics to be quite beautiful, and templates to be a vague approximation that shares many merits.

mainly, i just don’t like wading through all the angle-brackets, is all… especially if you’re nesting things. also, they throw out nearly completely unintelligable compiler errors when you get things wrong, and they make debugging on anything that’s not Visual Studio a complete nightmare.

not to mention, the oldskool assembly programmer in me balks at the idea of all that extra code being generated :wink:

chur
d

[quote author=“kylemcdonald”]I’m still transitioning to C++: sometimes I want my generic “utility” functions to do slightly different things, and I’m curious about conventions in C++ for these different things.

mapcar() replacement
If I have a collection (vector, queue, etc.) of things, and I want to do the same thing to a bunch of them, I’ll just write a function that iterates through them and does that thing. In LISP there’s mapcar() (or in Scheme map()). In Java, there’s no straightforward analogue. In C++, should I write a mapcar() function (really: template function) that takes a function pointer as an argument? Do people do this very often?

[/quote]

No, you don’t need to. First, #include , if it’s not already in OF (i haven’t looked at it much). Then, you have access to the Standard Template Library’s map class.

It’s implemented like this (roughly):

  
template <class KeyType, ValueType> class std::map;  

So, to use it, just make an object like this:

  
using namespace std;  
map<K,V>* ptr2map = new map<K,V>();  

Where K is whichever data type you’re using for the keyvalues (access keys) and V is the value types. You can retrieve (byreferenced) values like this:

  
K mykey = (whatever your access key is, sorry for the pseudocode injection);  
V* valueptr = ptr2map[mykey];  

Which is nice because you can then map values to string keys.

If you just want a numeric list, then I suggest either std::list or std::vector.

For list:

  
#include <list>  
  
using namespace std;  
  
list<ValType>* ptr2list = new list<ValType>();  

or for vector:

  
#include <vector>  
  
using namespace std;  
  
vector<ValType>* ptr2vec = new vector<ValType>();  

Modifying vs. Creating?

In C++, simply passing a value other than by reference or pointer simply copies the object (using default copy constructor (if applicable) otherwise mem-copying it).
You could make a non-byref function that uses the “new” keyword for dynamic allocation (THANK YOU BJARNE STROUSTRUP!) with a copy constructor (hint: Google “copy constructor” and learn!) that takes the copy as an argument, then returns the new pointer. Heck, you could make it a preprocessor macro if you wanted.

I have to admit, pointers took me a while to learn.

Basically, it doesn’t matter, since well-written programs work with pointers for:

  • Constant, non-copying access
  • Non-structuralized data buffers or entries (because a pointer can have memory-location-modifying math operators, as opposed to a reference type, which adopts the math operators of the referenced type)
  • Polymorphism, and base-class pointer downcasting
  • Complex, abstract “thinker” classes, instead of smaller “workhorse” types.
    And references for:
  • Call-by-reference function parameters, which don’t require that the user pass the pointer type or reference type, because it auto-casts from variable to reference within the function call
  • Smaller, numeric or C-style struct/union data types (time_t, for example)
  • Certain other things, which mainly pivot on coder’s preference.

And there are some other reasons, but I won’t waste too much E-ink.

You can DEREFERENCE a pointer (such as int*, or INT_PTR) by putting the * BEFORE it. This makes a REFERENCE from the pointer (such as int&, or INT_REF).

  
  
  
int* int_ptr = 0; /* NULL, as a pointer. (Implicit type-casting from numeric constant to reference, then assignment to pointer) */  
int& int_ref = 0; /* NULL, as an address/reference. (Implicit type-casting from numeric constant to reference) */  
  
int value = 1337;  
int_ref = &value; /* Gets the address/reference from the variable, converts to a numeric address */  
int_ptr = int_ref; /* Assignment of reference to pointer */  
  
  

References can then be cast to local values, or assigned to them. And it’s easy to make a pointer from a reference, as I’ve shown above.

[quote author=“kylemcdonald”]
Thank you my gurus![/quote]

You’re welcome.