C++ използва (void*) за прехвърляне към int за сравнение?

(Опитвам се) да напиша малко (работещ) C++ в проект ObjC :-) Библиотеката C++ (Box2D) ми предоставя клас b2Fixture, който има свойство „потребителски данни“ за програмистите, за да съхраняват всичко, което е подходящо за тях.

В моя случай той просто трябва да съхранява цяло число. От основната ми програма в ObjC едното е да прехвърлям цялото число към void*:

headFixture->SetUserData( (void*) 10 );

В полезен метод от страна на C++ на програмата бих искал да сравня потребителските данни с дадено цяло число (т.е. те са константи, 10 = твърда основа, 11 = платформа и т.н.).

Първото сравнение използва (void*), което отказва да се компилира. Открих на SO различен подход, както е илюстрирано от второто сравнение, което използва *( (intptr_t *) ...). Този се компилира, но изпраща EXC_BAD_ACCESS:

bool AbstractContactListener::contactContainsType(JRContact contact, int type){

    if (( type == ( (void *) contact.fixtureA->GetUserData() )) ||
        ( type == *( (intptr_t *) contact.fixtureB->GetUserData() ))
        ) {
        return true;
    }

    return false;
}

Изчерпват ми се идеите как да подходя към този проблем. Моля, помогнете :-) Благодаря! Дж.

РЕДАКТИРАНЕ/РЕШЕНИЕ:

bool AbstractContactListener::contactContainsType(JRContact contact, int type){

    if (( type == (intptr_t) contact.fixtureA->GetUserData() ) ||
        ( type == (intptr_t) contact.fixtureB->GetUserData() )
        ) {
        return true;
    };

    return false;
}

Този работи за мен!


person Jem    schedule 05.02.2012    source източник
comment
Ако дереферирате невалиден указател в *contact.fixtureB->GetUserData(), тогава, разбира се, ще се срине   -  person Seth Carnegie    schedule 06.02.2012
comment
Объркан съм защо трябва да използвате (void *). Съхранявате нещо като номер, нали. Или SetUserData съхранява указател към число? Ако можете да публикувате малко за това какво прави SetUserData, това ще помогне.   -  person octopusgrabbus    schedule 06.02.2012
comment
@octopus: SetUserData е библиотечна функция, която приема void*. Това е често срещан начин за разрешаване на данни, предоставени от потребителя. Обикновено съхранявате указател към вашите данни и след това static_cast до T*, когато ви бъде даден при обратно извикване или нещо подобно. Въпреки това, докато int не е по-голямо от void*, просто го кастинг към void* и след това обратно може да ви спести неприятностите при разпределяне на памет за това, което е константа.   -  person R. Martinho Fernandes    schedule 06.02.2012
comment
@octopusgrabbus Трябваше, защото SetUserData( 10) няма да се компилира поради семантичен проблем: Не може да се инициализира параметър от тип „void *“ с rvalue от тип „int“   -  person Jem    schedule 06.02.2012
comment
Хей, благодаря на всички за отделеното време. Вижте въпроса за редактиране за решението, което работи при мен.   -  person Jem    schedule 06.02.2012


Отговори (3)


Доколкото виждам, просто трябва да го хвърлите обратно; трябва да използвате целочислен тип, който гарантирано е със същия размер като указател за прехвърляне, или компилаторът може да изведе грешка;

#include <stdint.h>

if(type == (intptr_t)(contact.fixtureA->GetUserData())) {

Има обаче едно предупреждение, доколкото знам, прехвърлянето на int към void* и обратно не е гарантирано от стандарта C, за да върне същата стойност. По-сигурен вариант може да бъде просто да използвате указатели, тъй като те са предназначени да бъдат използвани;

int *value = malloc(sizeof(int));
*value = 10;
headFixture->SetUserData( value );

и

if(type == *(int*)(contact.fixtureA->GetUserData())) {

което не трябва да изкушава нито един компилатор да разбие кода ви.

person Joachim Isaksson    schedule 05.02.2012
comment
Хм, компилаторът изглежда не харесва това :( Семантичен проблем: Прехвърлянето от указател към по-малък тип 'int' губи информация. nb Използвам xcode, той има Apple LLVM Compiler 3 Благодаря :) - person Jem; 06.02.2012
comment
Вижте въпроса за редактиране за решението, което работи за мен... Благодаря за отделеното време! - person Jem; 06.02.2012
comment
Хей, интересна идея за локализиране на елемент с размер int. хубаво! - person Jem; 06.02.2012

Не разбирам точно какво се опитвате да направите, но вероятно трябва да прехвърлите данните обратно в типа, който искате да бъдат:

if (( type == reinterpret_cast<int>(contact.fixtureA->GetUserData() )) ||
    ( type == reinterpret_cast<int>(contact.fixtureB->GetUserData() ))
    ) {
person Pubby    schedule 05.02.2012
comment
Всъщност компилаторът не ми позволява да използвам == contact.fixtureA-›GetUserData(), тъй като не може да сравни указател с int (използвайки Apple LLVM Compiler 3 - може би това е причината?).. това е причината; -) Хм, използвайки вашето предложение, получавам Cast от указател към по-малък тип 'int' губи информация, което предотвратява компилирането :( - person Jem; 06.02.2012
comment
@jeM680000 Прочетете коментара на Fernandes. Нека void* да бъде указател към структура, която съдържа int. - person Pubby; 06.02.2012
comment
Хей, това не беше успешно. Но кастингът към (intptr_t) без използване на * свърши работа! Благодаря за отделеното време :) - person Jem; 06.02.2012
comment
int вероятно е 32-битова стойност, а указателят ви е 64-битов. (Ако разбирам това - GetUserData() връща указател - но вие просто го използвате като число). Ако случаят е такъв, прехвърлете void * към uint64_t и int към uint64_t, тогава просто направете сравнение. ЗАБЕЛЕЖКА: това може да се провали в система, която се нуждае от подравняване, като например някои ARM процесори. - person EdH; 06.02.2012

Искате да използвате reinterpret_cast<int>. Ако получавате грешка, значи не правите точно това, което казвате, че правите. (Вероятно се опитвате да дереферирате void * като int *.)

int stored_type = reinterpret_cast<int>(contact.fixtureA->GetUserData());
person moswald    schedule 05.02.2012
comment
Вижте въпроса за редактиране за решението, което работи за мен... Благодаря за отделеното време! - person Jem; 06.02.2012