Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Force lamba instances to have unique static variables

Tags:

c++

lambda

c++14

I would like to create a function that generates lambdas, where each of the lambdas has its own static variable. However, counter to what I thought would happen, the static variable seems to be shared between instances. For instance,

#include <iostream>

auto make_lambda(){
    return [](){
        static auto count = 0;
        return count++;
    };
}

int main() {
    auto a = make_lambda();
    auto b = make_lambda();
    std::cout << &a << ", " << a() << std::endl;
    std::cout << &b << ", " << b() << std::endl;
}

returns

0x7ffc229178df, 0
0x7ffc229178de, 1

So a and b seem to be unique instances, but are sharing that static count. I thought that I would see, and indeed want to see, something like

0x7ffc229178df, 0
0x7ffc229178de, 0

live demo

What is the simplest way to make sure that the lambdas each have their own static variable?

like image 820
bremen_matt Avatar asked Jun 04 '19 08:06

bremen_matt


3 Answers

Ditch the static variable and use an extended lambda capture:

#include <iostream>

auto make_lambda(){
    return [count = 0]() mutable {
        return count++;
    };
}

If you want different lambda instances to share state with their respective copies but not between them, you can use a std::shared_ptr instead:

auto make_lambda(){
    return [count = std::make_shared<int>(0)]() mutable {
        return (*count)++;
    };
}
like image 133
Quentin Avatar answered Oct 09 '22 11:10

Quentin


You can leverage the fact that instantiated functions, and the templated entities they enclose, have their own copies of static variables defined in the function. Turning make_lambda into a template...

template<int>
static auto make_lambda(){
    return [](){
        static auto count = 0;
        return count++;
    };
}

... will generate a new static variable for each new template argument, unique to the TU (on account of the function template itself being static):

auto a = make_lambda<0>();
auto b = make_lambda<1>();
std::cout << &a << ", " << a() << std::endl;
std::cout << &b << ", " << b() << std::endl;

Which is not quite the syntax you wanted, but gets the job done. If you don't mind involving the preprocessor and potentially compiler extensions, you can get the simple function call syntax with a helper macro.

#define make_lambda() make_lambda<__COUNTER__>()

where __COUNTER__ is a GCC extension that expands to a new number every time expansion is required in any single TU.

like image 27
StoryTeller - Unslander Monica Avatar answered Oct 09 '22 10:10

StoryTeller - Unslander Monica


You can pass a variable by value to the lambda. Moreover you have to make the lambda mutable so you'll be able to modify the value during execution. Normally labdas are like a const methods.

auto make_lambda(){
    int count = 0;
    return [count]() mutable {
        return count++;
    };
}
like image 28
woockashek Avatar answered Oct 09 '22 11:10

woockashek