views:

372

answers:

8

I am writing a library in C++ and have some functions that work with modules. An example would look like this:

void connect(Module *a, Module *b);

The problem is, that it would be sometimes handy if the function accepted also references (some of the Modules may be allocated on the stack and some on the heap and all the &s and *s get boring and messy soon).

Now I have inlined function that takes references, turns them into pointers and calls the original function.

inline void connect(Module &a, Module &b){
    connect(&a, &b);
}

I don't like this solution much, because for a few more functions it makes a lot of code to write, read, compile, ...

Another thing that was thinking about is adding Module::operator Module *() that would hust return this.

What are your thoughts on this? Isn't there any potential for epic failure that I missed?

Thanks.

+13  A: 

Why not just call the function with

connect(&a, &b);

like in your inline function, whenever you have to call it with references? This makes it very clear that the function takes pointers, and that a and b are not pointers. You only have to type two more characters.

Zifre
Mykola Golubyev
I think this is cleanest/easiest solution...it doesn't make sense to define a new function/use operator overloading when this mechanism is available.
thekidder
Always pass by reference unless a NULL pointer makes sense. If you get a pointer and check that is not NULL, pass it as a reference to other functions. Other than this I agree, just make one func and call it appropriately
caspin
+8  A: 

Any time you use operator overloading, you raise the possibility of epic fail. The problem is you know that you don't mean * as a standard pointer operator, but someone naively reading your code doesn't.

The best solution is to go back and refactor/rethink your code, so you don't need two interfaces for the same operation,

Charlie Martin
Once you have a conversion to a pointer type hell doors are wide open. Suddendly small mistakes in your code will not be flagged by the compiler. 'if ( obj )' becomes valid, just as '5 + obj' and many others that you probably did not intend... +1
David Rodríguez - dribeas
+1  A: 

I'd be very reluctant to do something like this - it would likely be a confusing interface to use.

In general, I'd prefer an interface that uses const references, but if the object passed in will be modified I tend to prefer pointers over non-const references because it gives the user of the interface an indication that the object being passed in might be modified.

Michael Burr
+1  A: 

The reason you need two functions is because a pointer-to-Module is fundamentally a different type than a reference-to-Module. There's no getting around that.

To me, pointers are bad. Always. Only use them if you need the address of something rather than the reference to something. And rarely do you need the actual address of an object. I'd do the following:

  1. Turn the pointer-based function into a reference-based function.
  2. Stop using pointers where unnecessary. You certainly should avoid having pointers and references.

Also, you might want to investigate const-ness.

dbrown0708
Never say "always" (or "never"). You can't have a NULL reference. You also can't have a reference to a reference. If you need NULL references or multiple levels of indirection, you should use pointers instead.
Adam Rosenfield
grrrrr, 'to me, pointers are always bad'. At least you said 'to me'.
KevinDTimm
You're both right. I should have gone with "things to avoid" rather than "never do this".
dbrown0708
There is almost nothing that should never be done. I used a Singleton design pattern a few years ago, and might well find a use for a "goto" before retirement.
David Thornley
goto? be gone! :)
KevinDTimm
+4  A: 

As you observed, you can overload the function to take either pointers or references, but I would resist doing so on the grounds of mere convenience and stick witn one function - this is how all the major libraries work.

Which argument type to use in that function? It depends - my personal guidelines:

  • If a NULL object can make sense use pointers.

  • If the common use case is that the objects being passed are normally created dynamically, use pointers.

  • If the implementation uses pointers internally, use pointers.

  • Otherwise, use references.

anon
A: 

I'd avoid inline in public API.

const & is preferred when possible over & or *

I have no idea how these things would be connected or what relationship an 'a' has to a 'b'

void connect(Module *a, Module *b);

Would it make sense to re arrange Module interface to be more explicit

Module a;
a.connectOutput(b);

naming your parameters helps with context ;)

Greg Domjan
+3  A: 

I am not sure if it is a good idea in your case, but generally, you could use an argument adapter:

struct ModulePtrOrRef
{
  Module * m_p;
  Module(Module * p) : m_p(p) {}
  Module(Module & p) : m_p(&p) {}
}

void connect(ModulePtrOrRef a, ModulePtrOrRef b)
{
  connect_impl(a.m_p, b.m_p);
}
peterchen
Nice solution. Much better than providing casts to pointers.
David Rodríguez - dribeas
+1 I like this.
+3  A: 

I always follow the convention listed in Google's C++ style guide:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments

Which basically says to only use const &, and * for everything else. I think it helps make very clear which parameters are inputs that cannot be modified (the const &'s), and which things are going to be modified (the *'s). If you follow such a convention, you can avoid digging through source code.

Pointers really aren't bad at all, so there's no reason to shy away from them. In fact, most of the time it will only mean using "->" instead of "." to dereference, which is cooler looking and very worth it to write clearer code.

Tom