DEV Community

Prabhu Ullagaddi
Prabhu Ullagaddi

Posted on

Keyword Arguments in C++/C

Introduction

In a programming language,function keyword arguments (aka named parameters) have following benefits (compared to the positional arguments of functions)

  • Improved readability of code
  • Eases code development
  • Avoids a class of bugs without human effort
  • Zero cost abstraction (aka Zero runtime overhead) (proof needed)
  • Provides backward compatible extensibility of your functions
  • Human friendly because we no more need to remember positions of args. At the same time we are very good in remembering tuples like (Name:Value) in everyday life!
  • Many more!

Python provides first class support for keyword arguments. There is an excellent article on Python keyword argument in the Post by Trey Hunner. C++ (or C) does not provide the same. However, we can achieve the same with a simple trick. This trick will be a Zero Cost Abstraction, hence we don’t need to worry about runtime cost due to this in C++/C yet we gain all benefits of keyword args.

Let us see how to simulate this in C++/C. Then we will discuss how it has some added advantages. If you need more justification for keyword arguments, this post [Why you shouldn't default to positional parameters] may be helpful. Please let me be a little non-technical before showing actual code. For those who used C/C++ but not Python/JavaScript/HTML/gRPC may not find the keyword args a great way to improve code. My view on s/w is: we develop code not just for machines to execute but also for fellow humans to read and maintain it. We must make every effort to make our code readable to our fellow people. Keyword arg is one such tool in our kit! Let us use it!

Simulating Keyword arguments in C++

This is achieved with designated internalizes of a class. Let me explain with an example.

Assume, you want to write a function that takes age and name of a person but accepts something similar to keyword args of Python.

/* to compile this program use c++17 std  as below.
bash$ g++ -std=c++17 1.cpp  && ./a.out 

if you want to use older c++ std, then you need to explicitly type cast the argument in function call to the type as declared in function declaration.
This would be similar to the C version I mentioned in later part of this article.
*/

/* simulated keyword arguments in C++/C */

#include <iostream>
using namespace std;

class Person {
public:
    std::string name;
    int age;
    int salary = 100;
};

void function(const Person& person) {
    cout << "Details of the person" << endl;
    cout << "  name: " << person.name << endl;
    cout << "   age: "<< person.age << endl;
    cout << "salary: "<< person.salary << endl;
    return;
}

int main(){
   function({.name="Alex", .age=18}); // salary is with default value of 100
   function({.name="Bob",  .age=21, .salary=200}); // salary is overridden
}
Output on 

p@p:~$ gcc -std=c17 1.c && ./a.out 
Details of the person
  name: Alex
   age: 18
salary: 0
Details of the person
  name: Bob
   age: 21
salary: 200
p@:~$ 
Enter fullscreen mode Exit fullscreen mode

Here, in function call, compiler automatically creates a temporary object of type as defined in function declaration. Hence explicit creation of a object of type Person is not needed. However, if you like to create one, there will be no issue but it will be needlessly verbose.

Note: if you see the variable “int salary = 100”, you may notice that this method also supports the default arguments facility of C++. Again, order does not matter here. ie, this method does not rob us from any advantage that is available to us in normal way. Please let me know if I am missing something here.

Simulating Keyword arguments in C
Since C too provides the designated initializes facility, we can use same. There is a little difference. C compilers do not create a temporary objects. Hence, we need to explicitly type cast (static type cast) during function call. Let us see this.

/* to compile this program.
bash$ gcc  1.c && ./a.out 
*/

/* simulated keyword arguments in C */

#include <stdio.h>

typedef struct Person {
    char name[100];
    int age;
    int salary;
} Person;

void function(const struct Person person) {
    printf("Details of the person\n");
    printf("  name: %s\n", person.name);
    printf("   age: %d\n", person.age);
    printf("salary: %d\n", person.salary);
    return;
}

int main(){
   function((Person){.name="Alex", .age=18}); //salary := default val of 100
   function((Person){.name="Bob",  .age=21, .salary=200}); // salary is 
overridden
   /*in another way, we can define a new var of type Person, assign values explicitly and pass. That is also a neat but it is verbose */
}
Enter fullscreen mode Exit fullscreen mode

Zero cost abstraction

Does this cost us some additional run time? I did not have time to investigate. However I am confident that this will have no extra runtime cost. Compilers are smart enough optimize the code. Interested persons can investigate this using dis-assembled code. For the best results, use the standard techniques such as

Data Padding: In struct / POD class declaration, put the variable that demands strictest alignment at the top and least alignment at the bottom and follow the gradient [padding]. This is a well known trick used in every struct/POD Class. Hence the same applies for simulation of keyword args too. One good thing is: During function call, we can mention the member variables in any order that seems natural to you. Compiler will take care of re-arranging in the order as you defined in struct/POD class declaration. By this, we understand, code is natural to read for human without sacrificing efficiency.

Additional benefits of this approach

Statically typed languages are popular and efficient since they have ability to eliminate certain class of bugs during compilation. Here, (aggregated) argument of a function itself is a type. So, this strongly binds function to its single argument. This way of aggregating function argument(s) into a class or struct is good and efficient mental model. i.e., we can think that every function takes only one argument and go on designing the software. Irrespective of, you agree or not to this, you can go and use simulated keyword argument as much as possible in your next project!

Conclusion

Code maintenance is a cost. We must use or explore ways of developing code that is readable in short and long term is a benefit to developers and company. Simulated Keyword arg is one such thing until C++ committee supports this.

References:

Top comments (0)