C++ Logo

std-proposals

Advanced search

[std-proposals] Function only accepts parameter that will persist

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Mon, 29 Apr 2024 13:42:44 +0100
Last week I was programming a microcontroller that had run out of heap
memory. There was a global 'std::map' object which mapped the name of
a command (e.g. "VERSION") to the address of the function that handled
that command, as follows:

    std::map< String, bool(*)(bool) > g_command_map;

I looked through the code to see where commands were being added to
the map, and I saw that every single one of them was a static-duration
constant string, using "operator[]" as follows:

    g_command_map["VERSION"] = &CommandHandler_Version;

Therefore the copying of the string into a heap-allocated 'String'
object was unnecessary. So I was able to write my own 'Map' class that
just stored a pointer to a null-terminated string, and I wrote
'Map::operator[]' to use 'std::strcmp' to check for equality.

Of course though, my new map class would malfunction if you gave it either:
    (1) A pointer to a static-duration string which would later change
    (2) A pointer to a stack string
    (3) A pointer to a heap string which would later change
    (4) A pointer to a heap string which would later be deallocated

Has anyone ever explored the idea of having a way of defining a
function which receives a pointer to an object which is guaranteed to
exist forever? So instead of having:

    void MyMapClass::operator[](char const *);

We might have something like:

    void MyMapClass::operator[](static char const *);

And so if you were to try to invoke the latter as follows:

    int main(void)
    {
        char str[] = "donkey";
        str[0] = 'm';
        mymap[str] = 123;
        str[0] = 'z';
    }

then you would get a compiler error because the object referred by the
decayed 'str' is not of static duration. Similarly the following would
give a compiler error:

    int main(void)
    {
        char *const str = new char[16u];
        strcpy(str, "monkey");
        mymap[str] = 123;
    }

In order to get the above snippet to compile, you would need to
express your guarantee to the compiler that the object referred by
'str' will exist forever, perhaps by just writing 'static' before the
argument:

    int main(void)
    {
        char *const str = new char[16u];
        strcpy(str, "monkey");
        mymap[ static str ] = 123;
    }

We would of course though need to consider the possibility of bugs
being introduced by programmers mis-using the 'static' keyword in this
context -- for example let's say you were to guarantee the immortality
of an object and then later deallocate it before the program ends.

Next we would have to discuss what effect 'const' has in the following
function signature:

    void MyMapClass::operator[](static char const *);

Does the 'const' mean that the function won't modify the string, or
does it mean that the string is guaranteed not to be changed elsewhere
for the remainder of the program? Well, here we could use 'const' with
'mutable' as follows:

    void MyMapClass::operator[](static char*) - Function cannot alter
object, but other functions can
    void MyMapClass::operator[](static char const*) - Function cannot
alter object, nor can other functions
    void MyMapClass::operator[](static char mutable*) - Function can
alter object, and so can other functions
    void MyMapClass::operator[](static char mutable const*) - Function
can alter object, but other functions cannot

We could also allow overloading, so that we could have two functions as follows:

    void MyMapClass::operator[](static char const *const p, int const x)
    {
        // No need to copy the string in here
    }

    void MyMapClass::operator[](char const *const p, int const x)
    {
        // We must copy the string in here
        char *const q = new char[strlen(p) + 1u];
        std::strcpy(q,p);
        (*this)[static q] = x; // Invoke the above function
    }

And we'd have to discuss how long "forever" lasts? Does it mean to the
end of the program? Or does it just mean until right before all of the
"atexit" registered functions are called?

Of course this is just a half-baked idea that I wish to float on the
mailing list to see if anyone can see any merit in it.

Received on 2024-04-29 12:42:57