Lambdas For C — Sort Of

A lot of programming languages these days feature lambda functions, or what I would be just as happy to call anonymous functions. Some people make a big deal out of these but the core idea is very simple. Sometimes you need a little snippet of code that you only need in one place — most commonly, as a callback function to pass another function — so why bother giving it a name? Depending on the language, there can be more to it that, especially if you get into closures and currying.

For example, in Python, the map function takes a function as an argument. Suppose you have a list and you want to capitalize each word in the list. A Python string has a capitalize method and you could write a loop to apply it to each element in the list. However, map and a lambda can do it more concisely:

map(lambda x: x.capitalize(), ['madam','im','adam'])

The anonymous function here takes an argument x and calls the capitalize method on it. The map call ensures that the anonymous function is called once for each item.

Modern C++ has lambda expressions. However, in C you have to define a function by name and pass a pointer — not a huge problem, but it can get messy if you have a lot of callback functions that you use only one time. It’s just hard to think up that many disposable function names. However, if you use gcc, there are some nonstandard C features you can use to get most of what you want out of lambda expressions.

Why GCC?

You have to use gcc, because this trick uses two nonstandard language features. First, gcc allows nested functions. In addition, you can use statement expressions. That is an expression that can do everything a function can.

These two things, combined with some abuse of the preprocessor, will let you stuff your function code right where you need it and relieve you of having to name each and every little function you write just to pass to another subroutine.

If you use another compiler, it might not — in fact, probably doesn’t — have these features. In the end, though, you could always just use a proper function with a pointer. This is a bit nicer.

Let’s Start

I created a simple example of a case where you might want to use a lambda in C. You can find it on GitHub. There’s an array of floating point numbers, thelist, and two functions that process the list. One averages the list after multiplying each value by 2. The other does the same task but divides each entry by 3 first.

A contrived example, to be sure, but easy enough to understand. If you study the code, the two averaging functions, average2x and averagethird, are almost the same. The only difference is the one point where the math is different.

Now look at this version. Here, the average_apply function does all the averaging work, but it requires a function pointer, fn, to do the math operation you want. There are two functions to work out the math:

float twox(float x) { return 2*x; }
float xdiv3(float x) { return x/3; }

Then making the call is easy:

printf("%f\n", average_apply(twox));
printf("%f\n", average_apply(xdiv3));

Of Course…

Those little snippets of code now need names and we won’t really use them anywhere else. That’s the perfect spot for a lambda. You can put this in a header file (although I just left it at the top of the file for this example):

#define lambda(lambda$_ret, lambda$_args, lambda$_body)\
({\
lambda$_ret lambda$__anon$ lambda$_args\
lambda$_body\
&lambda$__anon$;\
})

This takes advantage of the gcc features I mentioned and the fact that gcc will let you use dollar signs in identifiers. That means that you might need to pass -std=gnu99 on the gcc command line to make this work properly. This should do it:

gcc -o clambda2 --std=gnu99 clambda2.c

Now the calls just put the functions right in line:

printf("%f\n", average_apply(lambda(float,(float x),{ return 2*x; })));
printf("%f\n", average_apply(lambda(float,(float x),{ return x/3.0; })));

By the way, I realize you could just pass a factor of 2 or 0.3333 to one function, but that’s not the point. The functions could be as complex as you like. Like most programming problems, there are many ways to solve this.

Caveats

There are caveats. You can’t depend on the lambda having a scope beyond the enclosing function so an asynchronous callback would not work. Accessing local variables in the enclosing scope is iffy. Consider this code:

int factor=10;
printf("%f\n",average_apply(lambda(float,(float x),{ return x/factor; })));

This compiles and ought to work. It does work for this online compiler. However, using the Linux system for Windows 10, the same code would seg fault. In fact, if you didn’t set the gcc -O2 option, the other examples would seg fault, too. On my Linux boxes, it works fine. I can’t tell if this is a bug in Windows or if Linux is just covering up something wrong. However, it seems like if it compiles it ought to work and — mostly — it does.

In Practice

Granted, you don’t have to have lambdas with C. Even if you want them, they aren’t very portable. But it is still interesting to see how some gcc-specific compiler features work and it is also an interesting intellectual exercise to try to match it.

The technique is not new. If you search, you can find quite a few postings in various places with the same idea, although each one has small variations. Of course, in C++ of recent vintage you can use lambdas with no problem. By the way, lambdas are all the rage for cloud computing, too, and it is a similar idea. A little function that just runs without having to wait on a server for a request.

GCC logo GFDL.

45 thoughts on “Lambdas For C — Sort Of

  1. Oh geez. C was not intended to have this level of abstraction. C++, Python, etc have good reasons to have lambda functions (namespace pollution, closures, etc). Note that there are anonymous methods in C#.

    You kids can get off of my lawn. And you can pry my cold dead hands from my copy of ANSI/ISO C (IEC9899).

    1. Sandard C? Luxury!

      When I was young, our dad used to read from the K&R book to us every night. And that, let me tell you, did not have the red smear on the cover proclaiming “ANSI C” on it! That was good enough for us. Kids these days are spoiled, getting a new C++ standard every 2 or 3 years.

      1. You were lucky. We lived for three months in a core rope memory. We used to have to get up at six o’clock in the morning, flush the rom, eat a crust of stale bread, go to work down bus for fourteen hours a day week in-week out. When we got home, our Dad would thrash us to sleep with his belt! But you try and tell the young people today that… and they won’t believe ya’.

    2. C is great because you can do whatever you like from one extreme to the other… I use OO concepts in C, I use encapsulation for instance (through function pointers) even for embedded applications. Everyone is crazy about python, it was Rust, Java and many others before, but you know what ? C runs the world!

      typedef struct DNNA_SourceTypeDef
      {
      char path[DNNA_MAX_PATH_LENGHT];
      int fileDescriptor;
      short onError;
      DNNA_IO_Type ioType;
      DNNA_IO_Buffer buffer;
      DNNA_IO_BaudRate baudRate;

      /* — Methods — */

      int (*openMethod) (DNNA_SourceType *p_src, DNNA_IO_Type io, int buffer_size);
      int (*readMethod) (DNNA_SourceType *p_src);
      int (*writeMethod) (DNNA_SourceType *p_src);
      int (*closeMethod) (DNNA_SourceType *p_src);

      } DNNA_SourceType;

  2. As stated in the article, lambdafication is a feature that garners seems more attention than it might deserve.A I have seem seen folks essentially.claim that a.function like map demands a lambda .. obviously, it doesn’t.

    The preceding comment makes a great point that lambdas ido have their place .. a disposable function’s one-and-done nature can make things cleaner,. Also, a lambda has scoping rules that match the scope where it was it was defined. For example, a lambda that is declared within a non-static class method has access to het “self” values for objects of that class.

    So they are definitely nice.

    But they seem to be largely syntactic sugar that is layered on the ability to pass functions as arguments .. and nearly all modern languages seem to have first class functions.

    I’ll concede that I may be missing some essential capability of a lambda that can’t be supported by a “non-anon” function, and I would genuinely welcome being set straight if that’s the case.

    1. Damn, I am nearly certain that the text in the entry screen was not as garbled as the post was. I have seen weird stuff happen in editing boxes with the Android Chrome browser .. could it be that the text displayed in the box occasionally differs from the actual text posted?

    2. This kind of ‘lambda’ is just a simplification of the normal C notion of named procedure, separating out the naming and the procedure-creation. But in general ‘lambdas’ imply closures, which is a much more powerful concept: a closure also has access to its defining scope, which means that it lets you abstract over the data required to make a particular implementation of a function work.
      This is kind-of equivalent to passing a ‘data’ argument along with the function pointer, as is common in C, but has an important distinction: the *type* of the function is also abstracted over, and doesn’t need to leak into the interface of whatever calls the function. This can be done in C only with an indirection, e.g. a void*, which necessarily throws away some safety guarantees and performance.
      Of course, if you have existential types, they offer a more general mechanism for this kind of hiding. A lambda can be written as a pair of a value of existential type and a (non-capturing) function from that type.

  3. “The anonymous function here takes an argument x and calls the capitalize method on it. The map call ensures that the anonymous function is called once for each item.”

    Something, something, on a calculator.

  4. Please don’t do this to C. If lambda was a useful feature K&R would have included it. There are reasons C is feature limited, and it becomes quite apparent when you look at the wretched code written by modern programmers in less performant languages.

    1. Fundamentalism is never good. I think this is a nice hack and example what kind of specifics gcc has. However you have a point that modern “programmers” can write code that make you want to rip your eyes out, dont give them this kind of ideas please!

    2. “If lambda was a useful feature K&R would have included it.”
      If that were true, all development on all languages would have ended with the all-powerful work of Kernighan and Ritchie. Your inability to find usefulness in a thing does not make it useless. Many including myself have found great utility from languages and language features that have been added since 1978.

      Honest question – do you find difficulty finding employment when you restrict yourself to a 42 year old feature set?

      1. Sure, I find it difficult to cooperate and communicate with people who can’t program. Think I will now roll over and become a lamer? I have a pretty awesome track record and operate with confidence.

    3. All C code is wretched, filled with potential buffer overflows and incorrect arithmetic expectations. You’re a fool to expect anything else.

      You don’t make any sense at all. K&R were working on a PDP-11 with crippling memory and architecture constraints. Their version of “C” does not include “const” or function protoypes, shall we rip those out too? Oh and what about the “safe” version of sprint, snprintf? Shall we get rid of that? I have my beat old copy of K&R right here, next to the 68000 assembler manual, both old and dead.

      K&R imagined that all programmers were perfect and look where that got us! MITRE has an entire department of people whose job is to keep track of crappy C code and warn the rest of us about it.

  5. “If you try to call the nested function through its address after the containing function exits, all hell breaks loose. If you try to call it after a containing scope level exits, and if it refers to some of the variables that are no longer in scope, you may be lucky, but it’s not wise to take the risk.” — GCC docs

    1. “lambda$__anon$” becomes the function name. The final statement in the macro body, “&lambda$__anon$” effectively returns the address of the function.

      I’ve tinkered with this on godbolt.org, does anyone know if you actually need the dollars in the lambda? (It seems to compile without them on all the platforms that it compiles on with them, ie removing them doesn’t make a difference.)

    1. Come to that, I think I’d rather use a list comprehension to handle that python example:
      [x.capitalize() for x in [‘madam’,’im’,’adam’]]
      But could perfectly well be a generator if you don’t mind not being able to access the result without iterating it.
      bt I guessit’s all beside the point. Personally I just use lambdas where I need a quick one-off function, for example as an argument to sort()

  6. I’ve been programming since there was only assembly. And I’ve programmed in languages most have never heard of as well as all the ones mentioned here. Lambda have a legitimate use case I suppose, but, IMHO they just serve to obfuscate and make code hard to read…and give bragging rights to those who need them. 😎

    1. sorry, but anonymous functions are part-and-parcel of good program design. Lexical scoping means you don’t have to add hacks to get at your variables when you need them. Inlining the function means that information is local, you don’t need to repeat over and over again what a variable means in different places. Keeping excess stuff out of the namespace means LESS complexity, not more. Having to sequester a minor subroutine far from the action makes code harder to read, not easier.

      Oh and by the way, you are the one doing the bragging about your supposed skills, do you even know what the H in IMHO means?

      1. >>anonymous functions are part-and-parcel of good program design

        Respectfully, I disagree.

        Isn’t code more than just some text translated to instructions run by computer? Like a good book, code is also a means to convey my thought process to the reader, isn’t it? In such case, shouldn’t the reader be given first priority? Since not all readers are alike, shouldn’t it be in the language and structure that is understood with minimal cognitive efforts from the least skilled readers’ perspective?

        I’d rather have lengthy, well laid out code structure that even a first year intern could understand than having code that appears beautiful.

        I could have a thousand examples from highly successful enterprise products running code written in C, C++, Java, Python, JS even Forth. Made plenty of mistakes in the past twenty years and learned from them. The fundamental principle I learned my mistakes and others’ is simple. The current layer of code should present what it is doing. If the reader needs to know how it is done, he/she/they shall dig into further layers and/or modules. This worked out pretty well so far.

  7. Great idea: let’s abuse some compler-specific features to add unnecessary complexity to a language that already has plenty of bug-generating “features”.

    Why any insane person would do such a thing (sane people don’t use C)? Just add second argument to function, for example a char, that will be tested to see if the function should do one thing, or another.

  8. You did: map(lambda x: x.capitalize(), [‘madam’,’im’,’adam’]) which is 53 characters. Without the lamba, you get: map( str.capitalize, [‘madam’,’im’,’adam’] ) which is 45 characters. How does adding 8 character make it more concise?

  9. You poor little C programmers working for all those huge companies with huge programs to write and maintain, something that wouldn’t be possible in C++ if Lambda’s didn’t exist.

    In Assembly language all I have to do is cut and paste a function wherever I want it to be — no prototyping or labeling or overly-verboose syntax needed! It is the same thing done by your compiler whenever it encounters a Lambda in your code, except I think my code is more readable than yours is because nothing is hidden and everything is all in one place. And it would much simpler and readable to boot. So why is it you can’t do that? Afterall, you are just going to use that function just one, so why scatter bits and pieces of it everywhere throughout the rest of your code, when you could just put everything all in one place and accomplish the same thing?

    And don’t tell me that C wasn’t meant to as abstract as C++ is, because C++ was originally written in C by K&R (source code still available on the Internet). They wrote abstract concepts in C to use as concrete classes in C++. I’m pretty sure it would have looked every bit as much of a kludge as this ad hoc implementation of the Lambda in C code was. Lambda’s aren’t designed to make code more readable or maintainable for any of us, it was meant to provide job security for all the people who aren’t very logical or good programmers, yet they are still highly paid programmers anyway.

    So if some big business wants to pay some compiler manufacturer off to add yet another useless feature to a standard for something that no body else in the world asked them for, to solve a simple problem they had no idea how to solve, it can be done the same way it has been done in the past: by starting out writing it in Assembly or C first, and then translating it to C++/C#. The question is, why would anyone in their right mind want to do that, when a more simpler and easier way to do it existed, if only you actually knew what you were doing?

    1. In C we’re supposed to let the compiler worry about cutting and pasting the code. We don’t need to optimize that, we just need to lay it out in as simple, direct, and clear a way as possible so that the compiler can optimize it. Oh yeah, and so people can read it.

      If you have a big project, it is only a few hundred or thousand lines of code to build out a quality object system, and then the compiler throws it out as pastes it all together for you later. Lambdas are no advantage, merely a stylistic choice, if you assume a large project. Certainly for a Perl one-liner you might need them or you’ll get stuck with three lines.

Leave a Reply to RMS Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.