Hacker News new | past | comments | ask | show | jobs | submit login
A proposal for named arguments for C++ (open-std.org)
54 points by cpeterso on Oct 16, 2014 | hide | past | favorite | 61 comments



Please stop adding features to C++ and standardize an ABI already. The lack of an ABI is why it's impossible to call C++ libraries without an entire C++ compiler – and it generally requires generating mountains of wrapper code a la SWIG – which is then called with the C ABI. The lack of an ABI is why dynamic libraries use C for writing extensions instead of C++: C++ can literally only be called by C++ (and in fact only by the same C++ compiler).


There is an ABI most C++ vendors conform to already:

  http://www.swag.uwaterloo.ca/acd/docs/ItaniumC++ABI.htm
Almost every major vendor has been working towards this for a long time now.

Microsoft of course remains "special".

Regardless, the C++ standards committee did look at creating a portable C++ ABI in May 2014:

  https://isocpp.org/blog/2014/05/n4028


It's not really the responsibility of the standard committee, right? C also has no standard ABI.


C absolutely has an ABI – you do not need a C compiler to call a C library.


Nonsense. C doesn't even define the width of an int, without which an ABI makes no sense. An ABI is a property of a specific target platform (architecture and possibly OS); every platform defines its own. Windows and Linux on x86_64 don't even use the same ABI (the width of "long" is different, for starters). But both platforms have C++ ABIs just as much as they have C ABIs.

The problem with C++ ABIs is that C++ APIs tend to include much more substantial details in their headers. If you want to call a template or inline function, then you need a C++ compiler. Fundamentally, though, this is not any different from C macros -- a macro-heavy C API is going to require a C compiler to use.


It's certainly not cross-platform, but in practice you can call C libraries on a given platform the same way, regardless of which compiler generated them.


You can do that with C++ too -- assuming they are conforming to the platform ABI. On Linux, for example, all C++ compilers can call each other's code just fine.

You may be thinking of the funny situation on Windows where GCC has historically flagrantly disregarded the platform C++ ABI and implemented its own instead. I assume this decision was made as a matter of practicality: The Cygwin people didn't have the resources to try to implement Microsoft's ABI, whereas porting over the existing code for the Linux C++ ABI was a lot easier. Also, MSVC tends to take much longer to implement new C++ language features than GCC does which means its ABI could often be missing things GCC needs.

FWIW, Clang's port to Windows is taking the correct approach: they're implementing Microsoft's ABI, and will thus be able to link against MSVC-built C++ library. http://clang.llvm.org/docs/MSVCCompatibility.html


GCC didn't ignore the Windows C++ ABI. Windows doesn't have a platform C++ ABI.

Microsoft consider MSVC's C++ ABI as being internal to the compiler. They don't guarantee that the ABI won't change between major versions, only that it won't change between minor updates. Either way, it is not considered to be part of Windows, unlike the C ABI.

It's also not completely documented, so you'd have to reverse-engineer most of it if you wanted to be compatible.

It's the platform's de-facto C++ ABI, certainly, and in practice it's somewhat stable, but hardly reliable or useful for anyone other than Microsoft.

Other compilers and platforms used to have the same problem - each compiler had it's own ABI, and on platforms where one compiler was dominant, it basically became the de-facto standard, but there was no interoperability. That changed when pretty much every compiler (aside from MSVC) and OS (aside from Windows) adopted the Itanium C++ ABI, or a derivitive (like the ARM C++ ABI).

The C++ standards organization have a draft proposal to have platform standard ABIs, specified and documented by the platform holder. This really only affects Windows, and would require Microsoft to define a C++ ABI for Windows. That would most likely mean documenting the MSVC ABI, and calling that the standard C++ ABI.

Various Microsoft employees are pushing for it, so it might happen. The proposal was written by Herb Sutter, for example.


Hmm, you got me. I had thought MSVC's C++ ABI was documented and intended to be used by other compilers, but sounds like I got that wrong. Still, as you say, it is the case that most other platforms do in fact have a C++ ABI (if belatedly).


In all fairness, Microsoft breaks abi in the standard library with every single release. They do so deliberately to reserve the right to improve implementations over time.

For some evidence:

See an interesting reddit thread: http://www.reddit.com/r/cpp/comments/13zex3/can_vs2010_c_lib...

And a link to a msvc blog post which lists the sizes of various containers under various versions of MSVC: http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.a...

Note that vector<int> on x64 shrunk from 48 to 24 bytes between VC9 SP1 and VC11. That's not an ABI, it's source compatibility.


True, but that's a different level of ABI. Stefan is talking about the base language ABI, e.g. calling conventions, vtable layout, etc., which is what matters if you're trying to call C++ from another language. You're talking about the ABI of the standard library -- or, more accurately, any library that uses STL types in its interface (which probably disqualifies it from being called from another language anyway).


Its worse then that! The ABI is different for debug and release version of the same library resulting in crazy, crazy bugs.


So why are you asking that the standards committee do it?


You still need to know whether the C library expects stdcall, pascal, cdecl, several variants of fastcall, as well as a dozen less popular conventions - and that's just on x86.

Stdcall convention in C also often does some name mangling (leading underscore, trailing @ and the number of bytes on stack) but not always.

Also a C++ ABI would need to extend beyond simple calling conventions and also specify fixed behaviour of vtables, which i think compiler authors might balk at.


But not a standardized one, I think. Merely a few widely-used calling conventions (e.g. stdcall, cdecl).

Or am I misusing terminology?


The C ABI is a property of the platform, not the language itself.


A proposal for C++ to stop being such a moving target.


Luckily, this has already been proposed: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n402...

I really hope it makes it into the language. A stable API for external functions and whatever ABI you want for internal functions.


Oh, of course they cant do these simultaniously, right?

Or features are wrong? What's exactly wrong with this feature? Its improvement.


I think they should use this syntax:

void foo(static int name) { ... }

I don't think they're 'static' for anything on parameters yet.


Well played


Nice try.


This is exactly why I like the Objective-C method call syntax. Instead of "mystery argument soup"

    rectangle->set(10,10,20,20,true,false)
you get something that can be understood without taking a trip to the API docs:

    [rectangle setX:10 y:10 width:20 height:20 updateBSP:YES clipToParent:NO]
It's a pity they are moving away from this syntax in Swift. It's the right move -- I hear nothing but hate for it from those who don't actually use it (i.e. the people who should be Swift's target audience) -- but it's still a pity.


I think C++ should use notation similar to designated initializers (that C++ adapted from C99) for names arguments, i.e. rectangle->set(.x = 10, .y = 20, .width = 20, .height = 20)

Otherwise, it would be very confusing that two different syntax are used for similar purpose.


Swift has named arguments, and it's called "external parameter name". Here's an example taken from Apple's documentation[1].

  func join(string s1: String, toString s2: String, withJoiner joiner: String)
      -> String {
          return s1 + joiner + s2
  }
  join(string: "hello", toString: "world", withJoiner: ", ")
  // returns "hello, world"
[1]: https://developer.apple.com/library/ios/documentation/swift/...


One of my favorite C preprocessor hacks - emulating named parameters in C.

https://gist.github.com/robmccoll/5236408528c8c664c201


If only named member initializers were supported in C++.

Here's a similar hack that works in C++11, though it unfortunately requires declaring a global variable for each name (which I make less-bad by prefixing them with $, which is non-standard but supported by all major compilers):

https://gist.github.com/kentonv/7553792


I did something similar (without macros, with a bit more type safety since the keywords have an associated type, and with a shortcut for boolean arguments):

https://github.com/CaptainCrowbar/kwargs


Pretty cool hack, but not one I'd hope to run across in any real code. Between the nondescript function header and the inability to differentiate between an unset argument and a zero-set one, this macro would too quickly become a headache.


You can pre-initialize any member you want to something other than 0, such as -1. The C standard permits multiple initializations of the same member, and defines the value to be that of the last initialization. So if the application sets the member again, it all works properly.

Both GCC and clang complain about such initializer "overriding", but I habitually disable those warnings whenever I start a new project. (I also contributed the patch to clang to be able to disable that warning =)

Initializer overrides and missing braces are the two dumbest diagnostics that, sadly, both GCC and clang spit out. Using '{ 0 }' is the _only_ way to initialize a compound automatic variable where you do not know (and shouldn't depend on) the internal layout, and it's perfectly legal C.


This might sound negative, but I'm really curious why this is needed if you have an IDE that tells your the parameter names as you're typing and in a mouseover. If you need it to be more visible, make that a feature in the IDE to show you the parameter names all the time. Why change the language?

Not only that, but why are you passing so many parameters like top, bottom, left, right? At least put them in a struct if you can't make them fields of the object itself.

This feature seems like "too little, too late". Maybe 30 years about it would have been helpful.


Named parameters on their own are not all that helpful. (see Obj-C, where the verbosity often outweighs the potential benefits.) But when accompanied with having default parameter values allowing named parameters to be optional (as in Python), they can be very useful, allowing a whole family of functions to be consolidated into one fully general function that can nevertheless be called succinctly in a variety of ways.


Optional named parameters with default values can make nice clean calls in the common case while still allowing for the uncommon case.

As far as the IDE goes, for the sort of code I write, reading is more important than writing. When I read the code I'd rather see what it does than what I think it does.


VB used to allow you to omit optional parameters by just leaving it blank

foo(36,,,54)

sure it won't do if there are dozens but for the odd ugly case where you naughtily use optional parameters it's OK.

It sounds like there's a need for IDEs to show the parameter names all the time. They could just be inserted automatically into the display if you want to see them, or turn them off if you don't like that. Better than being always on like this proposal.


I like this idea in theory. the rectangle the example is a good one that I've personally tripped over before.

That being said, this could hurt readability in some cases as single lines of code are going to get larger, especially with length parameter names. Another issue I forsee is a code style where parameters are prefixed with p_. Having to type that prefix each time would be annoying, but finding a readable syntax that allows a separately specified name will also be confusing.


I agree this probably looks better in theory than it does in practice. As far as long function declarations go, it sure would be nice to go back to declaring multiple arguments of the same type without repeating the type:

  int myfunc(float x, y, z, r, g, b, a);
instead of

  int myfunc(float x, float y, float z, float r, float g, float b, float a);
Yes, I know about structs, but often that's not applicable. And if the type name is some long thing, then you have to really look carefully to notice they are all the same type. It seems this style is now being seen as a feature, although I don't know of any language that required repeating the type for each parameter until C and C++ adopted this for the new style function prototype declarations. I was very sorry to see that D has also adopted this "feature". I believe go allows multiple declarations of the same type. I'd much rather see this fixed in C and C++ than have them clutter up things with named parameters.


"int myfunc(float x, y, z, r, g, b, a);"

This got me thinking of the old C ways: http://msdn.microsoft.com/en-us/library/efx873ys.aspx


What's the argument against, well, named arguments? Is it a matter of brevity versus verbosity?


It is exposing more implementation details to the consumer of an API. A function with named parameters can’t rename its parameters without callers needing to update all their named arguments. Not necessarily a bad thing. And verbosity isn’t so much an issue—I don’t mind writing a bit more code now if it’ll save me from a bughunt later.

However, I do find named parameters suspect, not just because I don’t care for names generally, but also because they’re just papering over the problem of a function with many parameters, some of which have the same type.

Faced with that problem, I usually reach for more descriptive types: enums instead of booleans, numbers that know about their units and axes and coordinate spaces, that sort of thing. Ideally, all arguments have different types and there is only one permutation of their order that the compiler will accept. It sounds onerous, but most of your functions probably satisfy this constraint already.


Well, function specifications in the link table would have to become more verbose, and the linker would have additional decisions to make. You could also wind up deferring a lot of problems that could be caught by compile-time checking until link-time, which might also be run-time.

The problem that they're trying to solve is that people often use 5 arguments when a single struct would do.


No, this proposal does not affect the linker at all. Argument names would be resolved to positional arguments during compilation.


Yes, actually. You're right:

> In this proposal, the association of parameter names with a function for the purpose of making calls with named arguments to that function happens locally in each translation unit, and new declarations within a translation unit can change a function's ability to be called with named arguments at call sites below the new declaration (by using different names than a previous declaration).


The problem is that all the names of parameters in every header file suddenly become enshrined as part of the API. You end up having to remove the names from all the system header files or first standardise their names. Otherwise, you'll quickly find code starts appearing that assumes the names as they are on Linux will then not build on BSD, Solaris etc.


It is of some note that the order execution/passing of arguments to a C++ function is yet to be defined. For example,

    Whatdoesitdo (i++, i)


To pick nits, the order is defined: it's defined as being unspecified (Section 5.2.2).


Undefined is the opposite of defined, and threading in C++98 was `not considered` or `unaddressed`?


Given post increment, to me whatdoesitdo(i,i) then i = i+1 seems most likely. The whatdoesitdo(++i,i) case seems a bit more ambiguous once you add named parameters with reordering. Clang actually warns you if you try to do this. C or C++.


There is a proposal to fix this for C++17 http://isocpp.org/files/papers/n4228.pdf


I hate this idea. It just clutters the UI too much and adds noise especially if it's a UI you're familiar with. The C/C++ language is noisy enough without all this. Modern IDEs alleviate the need to be reminded inline what the values are for. On top of the fact, just about every IDE invented in the last 10 years offers code completion making this useless IMHO.


On the contrary, I would say that it cleans things up amazingly. As the language currently is, if I want to change one of the default values, I need to specify every single value up to that point.

    func(5,0,0,NULL,0,true,true,false,NULL,true);
    func(verbose: true);
With named parameters, I can just specify the differences between my function call and the default values.


> func(5, 0, 0, NULL, 0, true, true, false, NULL, true);

a Windows programmer by any chance? :)


This! I love the idea simply because of this use case.


Nope. Imagine that you change a function prototype:

    void drawBox(int X,int Y,int W,int H);

    void drawBox(int TL,int TR,int BL,int BR);
Having an extra safety for mature code offers an immediate protection, in a much more accessible fashion then competing ideas like 'concepts'.


What about reading? I can't always figure out what foo(0, xyz, null) means at first glance.


I can never remember the precise names of parameters. ;-)


I find the discussion about ABI compatibility to be unsatisfactory.

If I'm reading this correctly, it ends up basically boiling down to a preprocessor trick; you don't get the advantage of being able to add parameters to a function, and have name-based calls to it continue to do the right thing without a recompilation.


How about this:

  typedef int annotated_type;
  void foo (annotated_type parameter_name) {}
then I could write this:

  foo (5); /* the short and alegedly ambiguous version of function calling */
or this:

  foo ((annotated_type) 5); /* now I know more about what "5" means
                               in this function parameter's context */
There could be a lot of annotated types around, and their presence would be optional, so the code could be cluttered for the sake of making things more explicit only where it would be really needed!

Of course, this doesn't come with parameter-order juggling, but that is another can of worms in itself, which I'd better avoid!


I'm all for anything that can remove ambiguity from code where it is very likely. I notice this problem especially when I'm doing game stuff like collision detection and whatnot.


Why oh why do we need some parameters be positional and others be named. At the very least, it should just be one or the other style. Not some mix and match.


I can easily see a use for having some mandatory parameters followed by a few optional arguments. Requiring the mandatory parameters to be named adds verbosity to the code for the sake of slightly simplifying the language, which doesn't strike me as being a very C++ sort of philosophy.


Sure, why not - let's bolt another leg onto that dog ;)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: