C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Callsite passed as template parameter

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 16 Nov 2023 23:44:33 +0000
On Thu, Nov 16, 2023 at 8:38 PM Marcin Jaczewski wrote:
>
> Are you aware that the thing you propose is opposite of this?
> Do you know that each function `static` needs lock and it's not free.
> Or at least condition variable to check if the value was already initialized.


Not sure what you're saying here as your post is a bit vague, but if
you're referring to how since C++11 we've been guaranteed that
static-duration variables inside functions are thread-safe, and that
we therefore need a lock for each static variable inside a function,
well then let's just explore that -- consider the following code:

    #include <string> // string
    std::string &Func(char const c)
    {
        static std::string str(c, 20u);
        return str;
    }

This code will behave exactly as though you had written:

    #include <mutex> // call_once, once_flag
    #include <optional> // optional
    #include <string> // string
    std::once_flag myflag;
    std::optional<std::string> optional_str;
    std::string &Func(char const c)
    {
        std::call_once(myflag, [c]{ optional_str.emplace(c, 20u); });
        std::string &str = optional_str.value();
        return str;
    }

Ando so yes there's overhead here -- we have a lock. But that's only
because the constructor for std::string is complicated. Let's consider
a much more simple example:

    int &Func(void)
    {
        static int i = 77;
        return i;
    }

This will simply get compiled to:

    constinit int i = 77;

    int &Func(void)
    {
        return i;
    }

If we were to make it a little more complicated, then we would need a
lock, for example:

    int &Func(int const arg)
    {
        static int i = arg / 2;
        return i;
    }

would get compiled to:

    #include <mutex> // call_once, once_flag
    std::once_flag myflag;
    int i;
    int &Func(int const arg)
    {
        std::call_once(myflag, [arg]{ i = arg / 2; });
        return i;
    }

In my original example code that I gave in a previous post, I had a
char buffer that got initialised to all zeroes as follows:

    #include <cstring> // memcpy
    #include <algorithm> // min
    #include <string_view> // string_view
    template <typename = decltype([]{})>
    char const *cstr(std::string_view const sv)
    {
        static char buf[64u] = {};
        std::size_t const len = std::min(sizeof buf - 1u, sv.size());
        if ( len ) std::memcpy(buf, &sv.front(), len);
        buf[len] ='\0';
        return buf;
    }

There's no need for any lock here, the buffer will be get set to all
zeroes before the function is ever entered.


> In many cases a lot better would be pure `std::array` (or static vector)
> on stack and have a bigger stack to accommodate all data like this.
> This would have another benefits that program will not waste memory
> for things that are used only in one case and many "cases" have no
> overlapping lifetime.


Again not sure what you're saying here. I prefer to waste memory on
microcontrollers instead of using dynamic allocation. If I had my way,
my microcontrollers would have no heap.

Received on 2023-11-16 23:44:46