class XXX:now try to call it:
{
public:
void func(int aaa, string bbb, bool optionX = true);
...
};
func(1, "test", true);What irritates me here is, that I don't know what exactly does true mean at the first glance! Thus I found myself quite often writting code like this:
bool useOptionX = true;Well, that's better, but what about this:
func(1, "test", useOptionX);
bool useOptionX = false;a little bit misleading, isn't it? Not that this would be a big problem, but it's nevertheless a small hitch obstructing us from reading the code effortlesly. OK, next try:
func(1, "test", useOptionX); // should I use it or not?
bool dontUseOptionX = false;A total mess! And what about constructors?
func(1, "test", dontUseOptionX ); // don't use is false, so I should use it?
class XXX:
{
public:
XXXX(bool optionY);
...
};
class YYY: public XXXCall me pedantic, but I don't like that at all! What we really need here are Python's named arguments:
{
public:
YYY() : XXX(true) {}; // true what???
...
};
sameFuncInPython(aaa=1, bbb="pythonistas", optionX=True)Simple, effective, readable! Is there a way to emulate this in C++? Recently, as I was especially annoyed with the constructor example from above, I devised a following solution:
class XXX:
{
public:
XXXX(bool optionY);
...
typedef useOptionY bool;
};
class YYY: public XXXand now the other code can be wtritten totally plausibly too:
{
public:
YYY() : XXX(useOptionY(true)) {};
...
};
typedef bool useOptionX; // local namespace!
func(1, "test", useOptionX(false));and what would you say to the following?
func(1, "test", useOptionX(false));
waitOnSocket(sec(1), usec(20));
Small thing, but it improves my source code. I like it!
---
PS: After I wrote this, I remembered reading something in this vein about Java. And really, this post http://codemonkeyism.com/never-never-never-use-string-in-java-or-at-least-less-often/ by Codemokeyism proposes generally the same solution:
new Customer(firstName("Stephan"), name("Schmidt"));Alas, instead of a benign typedef, you have to write a whole new class named name and firstName etc. Without doubt it's and improvement, but the costs and the clutter... But on the other side Java tends to be verbose nonetheless, so maybe it's OK.
You'll love Boost.Parameter when you take a look there.
ReplyDeletehttp://www.boost.org/doc/libs/1_44_0/libs/parameter/doc/html/index.html
@Dean Michael Berris
ReplyDeleteHi Dean,
wow, this is a complete implementation of Python's named arguments!
But:
since I had some problems with ACE in the past, I took a dislike to big frameworks. And if you'll have a look at a Boost installation: it's huge! And there is so much interdependency! If a little nifty trick can spare me all that dependencies then I'm in for it!
Having said that, I'm not against Boost (well, not etirely), but in my current work I have to use another big framework already (Qt) so I don't want to mix it with another big one.
On the other side, at least one of Boost libraries advertises oneself saying that it hasn't dependencies on other parts of Boost, so maybe my fear isn't well-founded?
But nonetheless, thanks for the link! I've missed (or already forgotten) some Boost libraries as it seems, and I'd definitely use the Parameter library when designing a bigger interface!
This is nice, but only goes so far. If you had (choke) 13 string parameters, it would not help at all to make sure you didn't switch a couple. Still, nice tactic for readability.
ReplyDeleteThis is possible also:
ReplyDeletefunc(1, "test", /*optionX=*/true);
Inter-dependencies in Boost? Yes boost is huge, but not because its a huge library. Boost is huge, because it is a huge collection of independent libraries. Many of those are even header only (no installation, just copy and include).
ReplyDelete(although I admit, that I have no idea what exactly does this one specific sub-library require)
I tend to use the enum approach:
ReplyDeleteenum FormattingFlag
{
EnableFormatting,
DisableFormatting,
};
void parse(const char* str, FormattingFlag formatting);
\\ ...
parse("my string", EnableFormatting);
Readable, debuggable, with no ctors or dtors.
Spot the bug:
ReplyDeletevoid f(bool useX, bool useY);
typedef bool useX;
typedef bool useY;
f(useY(true), useX(false));
While the readability is increased, it can lead to a false sense of security. Kind of like how wrong comments are worse than no comments.
Using proper classes for this would let the C++ type system help you avoid mistakes like this. And if the class is just a POD class with one bool, there shouldn't be any overhead either.
I've got much-much simple (single header, almost no dependencies)
ReplyDeleteof the same concept here:
http://www.boost.org/doc/libs/1_44_0/boost/test/utils/named_params.hpp
@secret_town:
ReplyDeleteyes, it's not a panaceum...
@Emre B:
1st thought: cool!!!
2nd thought: I realized I did it sometimes before, but it wasn't estetically apealling ;-) to me
@Let_Me_Be:
If you have had a look at a Boost istallation directory (I've got 1.35.o) - it's huge and completely uncomprehensive. I bet there IS a lot of interdependecies (but I don't know for sure).
@Andrew Fray:
Ok, tht's the classical option. I'd use it to choose from several possiblities, but not for bool or int parameters.
@knatten:
ReplyDeleteyou've got a point here. It passed briefly my mind when writing, but then I discardd it as non-issue. But you're right, if the title of a post is "xxx for C++" then the reader expects rather a comprehensive solution.
So it's a question of tradeoff really:
1. typedef trick: lightweight, general, but no so type safe
2. enums by Andrew Fray (BTW I like it more and more as I think about it) - simple, typesafe, not general
3. classes with constructors by Codemonkeyism - heavyweight, typesafe, general, possibly killing performance
4. Boost - possibly the full solution, possibly dragging a ton of dependencies in...
Now decide!
Interesting post and comments.
ReplyDeleteWhat I like and use is the one similar to Andrew Fray's suggestion.
But, note that it changed the function signature from "bool" to "FormattingFlag". If I can change the signature, then I would go for that. If I can't change the signature of func, then I might do the following (a slight variation from the post):
const bool UseOptionX = true;
const bool DontUseOptionX = !UseOptionX;
My choice of placing them would be:
1. As static const inside class XXX, so that I can call func() as
func( 1, "test", XXX::UseOptionX );
2. In a place where all callers of XXX::func can see
3. In my namespace/module
@Arun Saha:
ReplyDeleteI think, my interface design was sloppy to start with. This whole bool optionXXX thing is crap. As you design carefully, you'll see that an option logically isn't a bool value, but an option value!
But I normally don't have time to think (sadly) and than have to improve things afterwards, as I won't go so far as to redesign such an interface. An that's what that trick is basically for, to somehow mitigate the technical debt if you want so.
Thanks for the comment(s), it improved the understanding of my own code!
Hi, very interesting post, greetings from Greece!
ReplyDelete