7

I'm wondering if it's possible in C++ to declare a function parameter that must be a string literal? My goal is to receive an object that I can keep only the pointer to and know it won't be free()ed out from under me (i.e. has application lifetime scope).

For example, say I have something like:

#include <string.h>

struct Example {
    Example(const char *s) : string(s) { }

    const char *string;
};

void f() {
    char *freeableFoo = strdup("foo");

    Example e(freeableFoo);     // e.string's lifetime is unknown
    Example e1("literalFoo");   // e1.string is always valid

    free(freeableFoo);

    // e.string is now invalid
}

As shown in the example, when freeableFoo is free()ed the e.string member becomes invalid. This happens without Example's awareness.

Obviously we can get around this if Example copies the string in its constructor, but I'd like to not allocate memory for a copy.

Is a declaration possible for Example's constructor that says "you must pass a string literal" (enforced at compile-time) so Example knows it doesn't have to copy the string and it knows its string pointer will be valid for the application's lifetime?

par
  • 16,746
  • 4
  • 62
  • 78
  • 1
    I think this is a dup of https://stackoverflow.com/questions/57543521/how-to-ensure-arguments-point-to-objects-in-static-storage-duration-only but that question doesn't have any answers either – CherryDT Sep 09 '21 at 20:29
  • What is its usage? – Ghasem Ramezani Sep 09 '21 at 20:30
  • @CherryDT I can see how what you're saying can happen, but it doesn't really change my question... I want to know if there's a way to enforce at compile time that a pointer's content can't change. – par Sep 09 '21 at 20:30
  • You can possibly achieve whatever it is you want to achieve using `std::unique_ptr` or `std::shared_ptr`. What is your ultimate goal here? – Galik Sep 09 '21 at 20:31
  • @par: I realized it can't happen, because the case I was thinking of was actually using macros to _appear_ to work this way, while in reality some additional things happened, it wasn't transparent. I now found out that string literals are guaranteed to have the lifetime of the program because they have static storage duration, so I deleted the original comment. – CherryDT Sep 09 '21 at 20:31
  • @CherryDT yes it looks like my question is a duplicate. I didn't find that one while searching the site, but basically it's the same question. Bummer :( – par Sep 09 '21 at 20:31
  • I"m pretty sure there isn't something like this on the constructor level. You may be able to declare the variable constexpr though, but applying constexpr to the constructor simply implies that the result can be a constexpr, not that it must be one (and you'd be able to pass `nullptr` too). – fabian Sep 09 '21 at 20:34
  • The goal is to just store a pointer and know it's immutable. In `Swift` for example a string can be declared `let foo = "foo"` and everything it is passed to will (behind the scenes) get the same pointer that will always be valid as long as there is a reference to it somewhere (and its content doesn't have to be copied). – par Sep 09 '21 at 20:34
  • @par that kind of logic is accomplished in C++ using `std::shared_ptr`. You are not going to be able to solve this with raw pointers, you need some kind of data management system, so use the one that the C++ standard provides for this task. To avoid copying the the string data when creating the initial `shared_ptr`, you could provide it with a raw pointer to the string literal and a do-nothing deleter. – Remy Lebeau Sep 09 '21 at 20:37
  • @RemyLebeau My embedded project doesn't have room for the C++ standard library. That's why a (say) `constexpr` argument would be useful. – par Sep 09 '21 at 20:40
  • You might be interested in [passing string literals as template parameters](https://stackoverflow.com/a/68790828/2752075). – HolyBlackCat Sep 09 '21 at 20:43
  • 3
    @par You can do it in C++20. Like [this](https://godbolt.org/z/fMoaP5cTa) Can simplify [a bit](https://godbolt.org/z/e3qWrMdqd) or make it easier on eyes. – C.M. Sep 09 '21 at 21:06
  • How many strings would you be copying here? Because if these are meant to be string *literals*, it would have to be an immense number of literals (ostensibly all manually typed) or objects holding copies of them for the size to make any appreciable difference. Are you sure you this isn't an attempt at premature optimization? – Joe Sep 09 '21 at 21:15
  • @Joe I working with about 18KB free heap currently, so I'm doing actual optimization ;) ... Besides, it seems like a useful language feature worth discussing. – par Sep 09 '21 at 21:42
  • @C.M. I don't have C++20 available for my project without building a new toolchain, but this seems like a great potential answer. – par Sep 09 '21 at 21:48
  • 1
    @par I don't see how you could make it without `consteval` (which is present only in C++20). Unless you find another mechanism that forces compiler to complain if expression/argument is not "known" at compile time. – C.M. Sep 09 '21 at 21:55
  • While not 100% bullet proof, we have been using `LiteralString` is our code base with no problems for years now. Here is a very simplified version: `struct LiteralString { template constexpr LiteralString(const char (&str)[N]) : string(str) {} const char* string; };` – prapin Sep 10 '21 at 08:42

1 Answers1

1

Just make the constructor explicitly taking rvalue:

struct Example {
   Example(const char*&& s) : string(s) { }

   const char* string;
};
Damir Tenishev
  • 750
  • 1
  • 9