0

I need to combine an int with a void*. I was thinking of doing it like :

long int lint = ((int)integer<<(sizeof(void*)*8)) | ((void*)ptr);

but according to my previous message, a long int won't suffice.

Can anyone suggest how to do this?

PS. Some people will ask why on earth I might want to do this. Well, I am developing a generic events system (dispatch/listen) that caters for countless different cases. I will post code when it's ready...

Community
  • 1
  • 1
Bill Kotsias
  • 3,258
  • 6
  • 33
  • 60
  • Do you mean you're trying to store a value which might be either an int or a void pointer? – LandonSchropp Feb 12 '12 at 09:30
  • 3
    Whatever you're trying to do, it's hopelessly non-portable in that form. Why aren't you using a plain old `struct`? – Mat Feb 12 '12 at 09:31
  • 3
    What's wrong with defining a `struct` that holds an `int` and a `void*`? – Sergey Kalinichenko Feb 12 '12 at 09:32
  • 2
    If I read your code right, you'd need an (at least) 96 bit integer type for this to work in 64 bit code. There is no such type in standard C++. Some compilers might offer a 128 bit integer type, but this wil always be a non-standard extension. As others have said, a struct is the way to go. – Sven Feb 12 '12 at 09:35
  • @bill - Whatever you are trying to achieve you are going about it in the wrong way. It is too complex, non-portable and looks messy and unreadable. Why not use a pointer to a `struct` or `union` or a combination of both. Easier to understand, portable and just as efficent as fiddling about bit shift (probably more so). – Ed Heal Feb 12 '12 at 09:42
  • The reason I didn't want to use a struct is because I *was hoping* that C++ had built-in types that would cater for this, i.e. types that are double the size of max(sizeof(int), sizeof(void*)). What about **long double**, would that always suffice? :-) – Bill Kotsias Feb 12 '12 at 09:42
  • @BillKotsias Does `std::pair` count as a built-in type? –  Feb 12 '12 at 09:45
  • @hvd No, I can't compare both values to another pair with a single instruction. A single compare instruction would be super fast and exactly what I am looking for... – Bill Kotsias Feb 12 '12 at 09:47
  • @BillKotsias: `long double`s aren't portable either, MSVC treats them as `double`s. – Necrolis Feb 12 '12 at 09:47
  • @ BillKotsias C++ is an almost complete superset of C, and the **built in** way to do this in C is with a struct. Stop trying to make things harder on yourself. – LandonSchropp Feb 12 '12 at 09:49
  • @BillKotsias That's not guaranteed to be available for any type, not even `char`. –  Feb 12 '12 at 09:50
  • 1
    OK people, you have convinced me. I'll go for a struct and let the compilers do the hard work (of optimizing) for me – Bill Kotsias Feb 12 '12 at 09:51
  • 1
    Iirc, x86 cannot compare integers > 32 bits in a single instruction anyway (without SSE), same for > 64 bits on x64. – Sven Feb 12 '12 at 09:54

4 Answers4

3

The only answer is to use struct

Don't try to be smarter than compiler. Don't perform premature optimization. Write simplest and clearest code and only after you see it too slow, perform low-level optimization

Struct are better here because:

  1. It is portable
  2. It safe
  3. It is c++ ideomatic.
  4. It is faster.

Let me bust your myths about comparing speed of long long and struct. As you know, all ways to optimize your code starts with profiling. Lets make simple program and measure comparing speed of vectors of long long and struct S:

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>

struct S
{
    unsigned int a;
    void* b;

    bool operator==(const S& other)  const
    {
        return a == other.a && b == other.b;
    }
};

template <typename Iterator>
int count_eq(Iterator begin, Iterator end)
{
    int result = 0;
    for (Iterator i  = begin; i != end; ++i) {
        for (Iterator j  = i + 1; j != end; ++j) {
            result += *i == *j;
        }
    }
    return result;
}

template <typename Iterator>
void mesure(Iterator begin, Iterator end)
{
    long long t0 = GetTickCount();
    int res = count_eq(begin, end);
    long long t1 = GetTickCount();
    std::cout << "result: " << res <<"; Time: "<<(t1-t0)<<"\n";
}

int main()
{
    const unsigned int Size = 20000;
    std::vector<unsigned long long> l;
    for (int i = 0; i < Size; i++) {
        l.push_back(i% (Size/10));
    }

    std::vector<S> s;
    for (int j = 0; j < Size; j++) {
        S el;
        el.a = j% (Size/10);
        el.b = 0;
        s.push_back(el);
    }

    mesure(l.begin(), l.end());
    mesure(s.begin(), s.end()); 
}

Lets check results:

>g++ src.cpp -O3
>a
result: 90000; Time: 327
result: 90000; Time: 188

Yes, struct with custom operator == 1.5 times faster.

Lol4t0
  • 12,444
  • 4
  • 29
  • 65
  • No it's not! http://ideone.com/KiNtH I suppose your compiler optimizes it because you always set void* = 0, so it skips that comparison. – Bill Kotsias Feb 12 '12 at 12:01
  • It is comiler specific. Same ideone shows opposit result for `c++` profile, as my compiler: http://ideone.com/uBkPE – Lol4t0 Feb 12 '12 at 13:16
  • @BillKotsias, also how can you be sure, that ideone does not use 64bit platform, where your code faster, but incorrect? – Lol4t0 Feb 12 '12 at 13:33
  • @BillKotsias: We don't know what optimization settings ideone is using. On my system (g++ 4.6.1), the struct version is faster with -O2 and -O3, but not with -O1. – Sven Feb 14 '12 at 08:04
1

Hmm shouldn't it be something like:

long long res = ((long long)integer<<(sizeof(void*))) | ((long long)ptr);

Thit will still only work for 32 bit pointers and will not work for 64 bit. There is no built-in type that will fit such arithmetics.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • 3
    My experience is that if it looks ugly it is the wrong solution. The above looks ugly. – Ed Heal Feb 12 '12 at 09:43
  • I agree and if I had to combine a pointer with an int I would not suggest this approach. However the question was how to combine them in this way so I am only pointing out the errors in what he proposes. – Ivaylo Strandjev Feb 12 '12 at 09:48
  • @EdHeal It's the "assembler" way of thinking. :-) – Bill Kotsias Feb 12 '12 at 09:48
  • 1
    @BillKotsias, but you write in `c++`, not `assembler`. – Lol4t0 Feb 12 '12 at 09:59
  • @Lol4t0 Sometimes, it helps to think in assembler and write in other languages - speed-wise that is. – Bill Kotsias Feb 12 '12 at 10:20
  • @BillKotsias, how can this code will increase speed? It's doubtful. But a looot of problems when you move to 64bit are guarantied. You should not ague with language you use. You should use rules it states, or move to another language – Lol4t0 Feb 12 '12 at 10:25
0

you should use something like uint_least64_t, available from stdint.h, of course it cannot hold pointers from 64 bits systems, for that you would need to use a struct as many people have mentioned in the comments.

Though the way you are using the pointers seems weird, it looks like you don't even need to use void* for what you are doing, nevermind the fact that you could to the same thing with polymorphism (for the event system you described).

Necrolis
  • 25,836
  • 3
  • 63
  • 101
  • 1
    It's guaranteed to be able to hold a pointer, and at the same time extremely unlikely to be able to hold more than that, so it's not an answer to the question. –  Feb 12 '12 at 09:33
  • As others have pointed out, it's very well possible that no large enough type exists, if you're dealing with a platform where pointers are already 64-bits. Not `uint_least64_t` either, which on such platforms is probably a typedef for the same type as `uintptr_t`. –  Feb 12 '12 at 09:43
  • @Hvd: was putting that in as you posted your comment. – Necrolis Feb 12 '12 at 09:46
0

If you want a data structure that can store either an int or a void pointer, use a union. A union will be the size of the largest value it contains. You can do something like this:

typedef union {
    void *data,
    int *value
} myUnion_t;

myUnion_t storage;
int num = 10;

// if you want to store a void pointer
storage.data = &num;

// if you want to store an int
storage.value = num;

Keep in mind a union is not a struct and should only store one value at a time.

LandonSchropp
  • 10,084
  • 22
  • 86
  • 149
  • He wants one value to hold both at the same time, so a union isn't the right answer (a regular struct is). – Sven Feb 12 '12 at 09:39