tags:

views:

862

answers:

7

I need to pass a pointer through a scripting language which just has a double and string type, for this I only have to worry about 32-bit pointers.

Seeing as the pointers are 32-bit, I figured doubles had enough precision to safely store the pointer, which works, however the problem arises with some pointers to stream, presumably due to multiple inheritance...and I'm really not sure how to solve it.

Basically I've been casting the pointers to unsigned, and then to double. Then to get them back I've cast the double to unsigned and then to whatever type the pointer is.

e.g.:

double example()
{
    int *i = new int[100];
    return (double)(unsigned)i;
}
double example2(double i)
{
    doSomething((int*)(unsigned)i);
}

However with the stream types it seems to not work...

std::ofstream *fs = new std::ofstream("example.txt");
std::cout << fs << std::endl;//029D1DF8 for example
double d = (double)(unsigned)fs;
std::ios *p = (std::ios*)(unsigned)d;
std::cout << p << std::endl;//029D1DF8 same thing seems fine
std::cout << ((std::ios*)fs) << std::endl;//029D1E54, opps, apparently a cast to std::ios changes the pointer to some offset!

Is there some way around this? I have got an idea using a map and id numbers but I'd rather avoid the cost of having such maps which may contain 1000's of entries. I much rather get the casting to work for this...

+2  A: 

I would use reinterpret_cast, and always cast to the same pointer type that you cast from to avoid any shifting around.

std::ofstream *fs = new std::ofstream("example.txt");
double d = reinterpret_cast<unsigned>(fs);
fs = reinterpret_cast<std::ofstream *>(static_cast<unsigned>(d));

Or else cast to std::ios first:

std::ofstream *fs = new std::ofstream("example.txt");
double d = reinterpret_cast<unsigned>(static_cast<std::ios*>(fs));
std::ios *p = reinterpret_cast<std::ios*>(static_cast<unsigned>(d));

When you're dealing with any kind of inheritance, it's crucial to always use the same class when casting to an opaque handle, and casting back from it.

Another possibility, but slower would be to encode the pointer value in a string. Why not store "029D1DF8" in a string?

Eclipse
The reason i'm casting to std::ios* in this case is because the object could be either std::stringstream or std::fstream, and I need them to be handled the same from the scripting languages point of view.
Fire Lancer
In that case, use the second example - cast to an std::ios before storing the pointer in the double. Whatever you want it to be when you pull it out is what it should be when you put it in.
Eclipse
hmm...and then if I cast the double to unsigned to std::ios* to say std::ofstream* I'm safe? (assuming the stream was origenally some form of output stream, or I use dynamic_cast, etc).
Fire Lancer
You'd have top use dynamic_cast to go from ios to ofstream. The compiler won't let you use static_cast and reinterpret_cast would give you the wrong results.
Eclipse
If you want a (very) little bit of type-safety for types that have at least one virtual function, you can look at my answer to this question.http://stackoverflow.com/questions/311102/safely-checking-the-type-of-a-variable/324739#324739
Eclipse
you can also cast after reading the int. but cast to ofstream*, then to ios*. so the compiler can do the right adjustment
Johannes Schaub - litb
The standard-conformant way to downcast is dynamic_cast.
Thomas L Holaday
+1  A: 

Try reinterpret_cast:

double example()
{
    int *i = new int[100];
    return static_cast<double>(reinterpret_cast<unsigned int>(i));
}
rlbond
+1  A: 

You are really playing with fire here. Conversions from ints to doubles and vice versa involve the representation of floating point and you might lose accuracy which does matter in pointers.

I'm not really clear why you are doing conversions outside the interface points of your system and the scripting language. Isolate that and then, if you just want to represent addresses, use a void* or the real type of what you're representing.

I can't really think of a use case of passing pointers to a scripting language anyway. Can you explain more what it is that you are trying to do ?

Uri
I cant pass the pointer as void* or whatever to the scriptm else I would have...doubles can represent any 32bit integer so accuracy is not a problem. Cause as I said I don't really want to use an std::map<double, Someclass*> for an id systemwhich would contain 1000's of entries if casts will do it..
Fire Lancer
I'm not worried about accuracy in terms of doubles storing 32 bit. I'm worried about your conversions from doubles to ints and vice versa.
Uri
+2  A: 

Of course casting pointers to doubles and back is inherently dubious and nonportable, but in an inheritance tree where multiple inheritance is involved, it will not work without additional kludging.

Consider this:

#include <iostream>
#include <fstream>

int main(int, char **) {
  std::ofstream *ofs = new std::ofstream("example.txt");
  std::ios *fs = ofs; // aim fs at the ios portion of the fstream
  std::cout << ofs << std::endl << fs << std::endl;
  delete ofs;
  return 0;
}

Output:

0x800000
0x80020c

Note that even though you have assigned the ofstream pointer to the ios pointer, the values differ. You need a downcast, and the correct way to do the downcast is with dynamic_cast:

#include <iostream>
#include <fstream>

int main(int, char **) {
  std::ofstream *ofs = new std::ofstream("example.txt");

  // aim fs at the ios portion of the fstream
  std::ios *fs = ofs;  

  // print 'em
  std::cout << ofs << std::endl << fs << std::endl;

  // aim ofs2 at the fstream portion of ofstream object 
  // you know lurks behind the ios pointer
  std::ofstream *ofs2 = dynamic_cast<std::ofstream *>(fs) ; 

  // print it
  std::cout << ofs2 << std::endl ;

  delete ofs;
  return 0;
}

Output:

0x800000
0x80020c
0x800000

Putting it all together, you can use:

std::ofstream *ofs = new std::ofstream("example.txt");
double d = static_cast<double>(reinterpret_cast<unsigned long>(ofs));
// ... do something with d ...

// ... find the ios part of the ofstream ...
std::ios *fs = reinterpret_cast<std::ofstream *>(static_cast<unsigned long>(d));

// ... get the ofstream back ...
std::ofstream *ofs2 = dynamic_cast<std::ofstream *>(fs);

It is not clear from the question why you need both an ios and an ofstream.

Thomas L Holaday
A: 

Is it safe to assume that your application controls both the sending and the receiving of these pointers -- i.e., that the pointer won't be picked up by code that you don't control?

If that's the case, then I suggest you don't pass pointers at all. Keep the pointers in a hash table, array, or linked list associated with the script engine, and pass an integer index (or key) into that data structure.

This may feel like a high overhead solution. However, the overhead is likely to be much lower than the overhead of using scripting within your app, so it probably will not be measurable.

EDIT: OK, I see that you included the idea of using a map. (I missed the first time because it's tucked after the code block.) But even for 1000s of entries, this is not likely to be your performance bottleneck.

Dan Breslau
+2  A: 

You're casting from ofstream forward, and then back to ios.

You need to cast to and from the same type. I would use ostream.

Oh, and it's better to use size_t rather than unsigned (does double have enough precision on 64 bit machines? Doubt it.).

std::ofstream *fs = new std::ofstream("example.txt");
double d = (double)(size_t)static_cast<std::ostream*>(fs);
std::ostream *p = reinterpret_cast<std::ostream*>((unsigned)d);

Should you want to get back to ofstream, you need to use dynamic_cast and be prepared to deal with NULL.

Arkadiy
No, don't use size_t either. Use ptrdiff_t.
Joshua
They should be same size, but you are right.
Arkadiy
A: 

Ok, the solution I came up with was to simplfy the casts.

I defined a structure, like the one below that stored pointers already cast to the types I wanted.

struct Streams
{
    std::istream *is;
    std::ostream *os;
    std::stringstream *ss;
    std::fstream *fs;
};

I then populated the struct (setting the ones that were not supported by the "real" object to null),having allocated it on the heap and then just casted a double to return to my script, which is then safe to cast back to Streams*.

this also had the added advantage of allowing for safe checking that the stream passed to a method support that operation, simply by checking if the type used for that method (eg ss for the str() method of string streams) was not null.

Fire Lancer