-1

I want to use addresses of objects, as unique identifier, when I am storing the state of my simulation in a json file. I therefore convert the pointer to my object into a uintptr_t.

Cat myCatptr = new Cat;
uintptr_t address= (uintptr_t)myCatptr;

Now I convert the address into a QString:

QString str = QString::number(address); 

I can then easily add the QString to a QJsonObject. But how can I convert back from QString to uintptr_t?

On my machine I know that sizeof(unsigned long long) and sizeof(uintptr_t ) are both 8byte. Hence I can use

address= (uintptr_t)str.toULongLong();

Is this always correct, independent of the platform? This would require that sizeof(uintptr_t) <= sizeof(unsigned long long) for all platforms.

As stated here, sizeof(unsigned long long) is always >= 8byte.

Again in summary:

  1. Is my Ansatz, converting it to unsigned long long and then to uintptr_t always working?

  2. If not, do you know an alternative that works on any platform?

Community
  • 1
  • 1
newandlost
  • 935
  • 2
  • 10
  • 21
  • 2
    As soon as you say *"I know on my machine `sizeof`..."* then when the inevitable question of *"is this platform independent"* comes up, the answer usually is no. – Cory Kramer Apr 19 '17 at 12:26
  • [`uintptr_t` (optional)](http://en.cppreference.com/w/cpp/types/integer) is not even required to be available on all systems. But likely on those supported by Qt! – Bo Persson Apr 19 '17 at 12:45
  • @Bo Qt has [`quintptr`](http://doc.qt.io/qt-5/qtglobal.html#quintptr-typedef) for that. – rubenvb Apr 19 '17 at 15:00
  • Using addresses of objects as unique identifiers is a terrible practice. – rubenvb Apr 19 '17 at 15:01
  • why? I just want to get a easy visualization, just for a single run. The memory does not get reallocated by design of my simulation. I am working with graphs. Can you give me more details on why it is not good practice? – newandlost Apr 19 '17 at 17:36

3 Answers3

2

Is it guaranteed by the standard? No. It is perfectly feasible to imagine a platform where qulonglong is 64 bits, but uintptr_t is some magic 256 bit extended type.

On the other hand, a) no such platform currently exists; b) Qt might well define qulonglong as 256 bits on that platform, even if unsigned long long was 64 bits; c) you don't have much choice: given a QString, the longest integer you can easily turn it into is qulonglong.

The 100% portable solution is to use scanf and SCNxPTR (which is the format to convert text into uintptr_t.

    const auto text = str.toStdString();            // Convert to std::string
    sscanf( text.c_str(), "%" SCNxPTR, &address );  // Convert to uintptr_t
    // additional error handling required.

Documentation of scanf in general here. The general form is:

sscanf( pointer_to_const_char_array, format, &value1, &value2, ... );

The documentation of uintptr_t and SCNxPTR is here (and I actually found it via this answer. SCNxPTR is a #define macro which expands to a string literal which is the right format to feed to sscanf (and others in the scanf family) to decode a uintptr_t in hex.

Community
  • 1
  • 1
  • Could you please explain the syntax more detailed? I never used sscanf. How do you know we have to use `SCNxPTR` ? – newandlost Apr 19 '17 at 13:40
  • @newandlost - does that help? If you meant "how did I know to look for SCNxPTR?" - I had encountered the `PRI?` macros before (for printf), so searched for "scanf and uintptr_t". – Martin Bonner supports Monica Apr 19 '17 at 18:10
0

It should work fine, there is certainly enough space to fit a pointer regardless of which contemporary platform you are targeting. Actually, you might be going a bit overboard, you could just directly cast pointers to qulonglong and back. I've tested this on x86 and arm, 32bit and 64 bit builds.

You could also take whatever size the pointer happens to be on a particular platform, and move that into a byte array, then create a string from that data in base64 encoding, and do the opposite when you convert back. This should have you backed up regardless of the pointer size on that platform.

There is another, and better way. You could implement a QHash<int, Cat *> registry, and instead of using pointers directly, you assign a unique integer id to each cat, and associate it with the address in memory. This has an advantage of being persistent across application runs, so you can always address that particular object even when its address changes every time you recreate the application data on loading. Since you mention "storing the state" I assume you'd want to store it in a manner that persists across application run. And there is zero guarantee that you will, and it is pretty much impossible to allocate the object at the same address the next time the application runs, thus any saved pointer values will be useless. Thus the optional solution is to associate the actual pointer to an integer id that will persist, the lookup will also likely be faster than converting strings to integers back and forth.

dtech
  • 47,916
  • 17
  • 112
  • 190
0

I also think I found a solution. Maybe you could comment on it, if you see any mistake.

My solution uses QVariant. I register the datatype uintptr_t such that it is know by QVariant:

Q_DECLARE_METATYPE(uintptr_t); which is defined in Qt core.

Then...

Strand * myCatptr = strand;
uintptr_t address= (uintptr_t)myCatptr;
QString str = QString::number(address);

//string can be passed to QJsonObject
//...
//string gets loaded from QJasonObject again to a later point.

//Variant can be converted to uintptr_t, because I registered it
QVariant var = QVariant::fromValue(str);
address = var.value<uintptr_t>();

Which converts the QString to a uintptr_t, via QVariant.

newandlost
  • 935
  • 2
  • 10
  • 21