Проблема приведения указателя в C ++ - PullRequest
0 голосов
/ 14 апреля 2009

Мне нужно пропустить указатель через язык сценариев, который имеет только двойной и строковый тип, для этого мне нужно беспокоиться только о 32-битных указателях.

Учитывая, что указатели являются 32-разрядными, я полагал, что double имеет достаточную точность для безопасного хранения указателя, что работает, однако возникает проблема с некоторыми указателями для потоковой передачи, предположительно из-за множественного наследования ... и я действительно не уверен, как ее решить.

В основном я приводил указатели к unsigned, а затем к double. Затем, чтобы вернуть их, я приведу double к unsigned, а затем к любому типу указателя.

например:.

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

Однако с типами потоков это, похоже, не работает ...

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!

Есть ли способ обойти это? У меня есть идея, используя карту и идентификационные номера, но я бы предпочел избежать стоимости таких карт, которые могут содержать тысячи записей. Я бы предпочел, чтобы кастинг работал на это ...

Ответы [ 7 ]

2 голосов
/ 14 апреля 2009

Вы кастуете из ofstream вперед, а затем обратно на ios.

Вам нужно разыграть одного и того же типа. Я бы использовал ostream.

О, и лучше использовать size_t, а не unsigned (достаточно ли двойной точности на 64-битных машинах? Сомневаюсь.).

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);

Если вы хотите вернуться к ofstream, вам нужно использовать dynamic_cast и быть готовым к работе с NULL.

2 голосов
/ 14 апреля 2009

Конечно, приведение указателей к значениям типа double и обратно по своей сути сомнительно и непереносимо, но в дереве наследования, где используется множественное наследование, оно не будет работать без дополнительных ключей.

Учтите это:

#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;
}

Выход:

0x800000
0x80020c

Обратите внимание, что даже если вы присвоили указатель ofstream указателю ios, значения различаются. Вам нужен downcast, и правильный способ сделать downcast с помощью 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;
}

Выход:

0x800000
0x80020c
0x800000

Собрав все вместе, вы можете использовать:

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);

Из вопроса неясно, зачем вам нужен как ios, так и ofstream.

2 голосов
/ 14 апреля 2009

Я бы использовал reinterpret_cast и всегда приводил к тому же типу указателя, с которого вы применяете, чтобы избежать любого смещения.

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

Или сначала приведите к std :: ios:

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));

Когда вы имеете дело с любым видом наследования, крайне важно всегда использовать один и тот же класс при приведении к непрозрачному дескриптору и приведении к нему обратно.

Другая возможность, но медленнее, заключалась бы в кодировании значения указателя в строке. Почему бы не хранить "029D1DF8" в строке?

1 голос
/ 14 апреля 2009

Вы действительно играете с огнем здесь. Преобразования из целых в двойные и наоборот включают представление с плавающей запятой, и вы можете потерять точность, которая имеет значение в указателях.

Мне не совсем понятно, почему вы делаете преобразования вне точек интерфейса вашей системы и языка сценариев. Изолируйте это, а затем, если вы просто хотите представить адреса, используйте void * или реальный тип того, что вы представляете.

Я не могу придумать случай использования передачи указателей на язык сценариев в любом случае. Можете ли вы объяснить больше, что вы пытаетесь сделать?

1 голос
/ 14 апреля 2009

Попробуйте reinterpret_cast:

double example()
{
    int *i = new int[100];
    return static_cast<double>(reinterpret_cast<unsigned int>(i));
}
0 голосов
/ 08 мая 2009

Хорошо, решение, которое я придумал, состояло в том, чтобы упростить приведение.

Я определил структуру, подобную приведенной ниже, в которой хранятся указатели, уже приведенные к нужным типам.

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

Затем я заполнил структуру (установив те, которые не поддерживаются "реальным" объектом, в null), выделив ее в куче, а затем просто бросил double, чтобы вернуться к моему сценарию, который затем можно безопасно преобразовать назад к потокам *.

это также имело дополнительное преимущество, позволяя безопасно проверять, что поток передан методу, поддерживающему эту операцию, просто проверяя, был ли тип, используемый для этого метода (например, ss для метода str () для потоков строк) нуль.

0 голосов
/ 14 апреля 2009

Безопасно ли предполагать, что ваше приложение контролирует отправку и получение этих указателей, т. Е. Указатель не будет выбран кодом, который вы не контролируете?

Если это так, то я предлагаю вам вообще не передавать указатели. Сохраните указатели в хеш-таблице, массиве или связанном списке, связанном с обработчиком сценариев, и передайте целочисленный индекс (или ключ) в эту структуру данных.

Это может быть похоже на решение с высокими накладными расходами. Тем не менее, накладные расходы, вероятно, будут намного ниже, чем накладные расходы на использование сценариев в вашем приложении, поэтому они, вероятно, не будут измеримы.

РЕДАКТИРОВАТЬ: ОК, я вижу, что вы включили идею использования карты. (Я пропустил первый раз, потому что он спрятан после блока кода.) Но даже для тысяч записей это вряд ли станет вашим узким местом в производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...