5

Is there a function in C++ (C++11, if it makes a difference) that converts a string to a uintptr_t or intptr_t? I can always use atoll() and cast it afterward, but it'd be nice to get a function that does 32-bit for 32-bit machines and 64-bit for 64-bit machines.

char* c = "1234567";
uintptr_t ptr = atoptr(c); // a function that does this;
atanamir
  • 4,833
  • 3
  • 24
  • 20
  • Have you tried a std::istringstream? – Tony Delroy Apr 18 '14 at 00:26
  • I am not sure it is a good idea reading a string to a "int" with a range that depends on the platform. And since atoi() is definitively not a function to be used in real life, you're back to strtol which always return a long. By the way are you really reading pointers from string?? – Joky Apr 18 '14 at 00:37
  • @Joky this in particular is for windows programming; i'm creating a process with `bInheritHandles` and apparently the way to tell the child process what the handle is by passing the handle's value via the command line. Now i'm trying to parse it back into the handle (which is declared as `void*`). – atanamir Apr 18 '14 at 00:39
  • @TonyD i'll look at `istringstream` tbh I haven't used it before, but it looks like it might be what I want. – atanamir Apr 18 '14 at 00:42
  • 1
    @atanamir just "std::istringstream iss("1234567"); if (iss >> std::hex >> ptr) ...conversion succeeded..." - remove hex if decimal. – Tony Delroy Apr 18 '14 at 01:25
  • intptr_t result; if (sizeof(intptr_t) <= sizeof(long)) result = strtol(c, NULL, 10); // or 16 for hex – tobi_s Sep 26 '18 at 02:24

1 Answers1

2

This is an IMO surprising gap in C++. While stringstream does the job, it is quite a heavy tool for such a simple task. Instead, you can write an inline function that calls the correct variation of strtoul based on the type sizes. Since the compiler knows the correct size it will be smart enough to replace calls to the function with calls to strtoul or strtoull. I.e., something like the following:

    inline uintptr_t handleFromString(const char *c, int base = 16)
    {
         // See if this function catches all possibilities.
         // If it doesn't, the function would have to be amended
         // whenever you add a combination of architecture and
         // compiler that is not yet addressed.
         static_assert(sizeof(uintptr_t) == sizeof(unsigned long)
             || sizeof(uintptr_t) == sizeof(unsigned long long),
             "Please add string to handle conversion for this architecture.");

         // Now choose the correct function ...
         if (sizeof(uintptr_t) == sizeof(unsigned long)) {
             return strtoul(c, nullptr, base);
         }

         // All other options exhausted, sizeof(uintptr_t) == sizeof(unsigned long long))
         return strtoull(c, nullptr, base);
    }

This will be easy to update if you ever decide to change the type of the handle. If you like angle brackets, you can also do something equivalent with templates though I don't see how that could be clearer.

Lastly, you can also use sscanf with the %tx format, i.e.

inline uintptr_t handleFromString(const char *c)
{
   ptrdiff_t h;
   sscanf(c, "%tx", &h); // only hex supported, %td for decimal.
   return (uintptr_t)h;
}

Unfortunately, no compiler that I tried on the Compiler Explorer managed to optimize the code in a way that removes the call to sscanf.

tobi_s
  • 1,324
  • 13
  • 16
  • Which part of the standard justifies the `static_assert()`? I can't see anything that requires `std::uintptr_t` to be the same size as *any* other type. – Toby Speight Oct 12 '18 at 08:10
  • 1
    The static_assert is there exactly because the function may not exhaust all possibilities. This would imply that the program would have to be changed. To me that is the purpose of `static_assert`, so I didn't make this explicit, but I'll make the comment state such. Thanks! – tobi_s Oct 14 '18 at 02:45
  • What would the use case for this function be? – M.M Oct 14 '18 at 04:14
  • I tried to make the comments in the function yet clearer. The purpose would be to be able to use the same function for the string to `uintptr_t` conversion described in the original post while still always calling the appropriate function for the actual data types on the target platform as requested in the original post. I think this is an academical question but I can come up with use-cases such as an embedded target where you might not want something as heavy as stringstream. Even "call strtoull followed by a typecast" instead of "strtoul" might be too heavy. – tobi_s Oct 14 '18 at 04:59