Строгое искажение указателя: какое-либо решение для определенной проблемы?

Мне вызвали проблему путем нарушения строгого правила искажения указателя. У меня есть тип T это прибывает из шаблона и некоторого целочисленного типа Int из того же размера (как с sizeof). Мой код по существу делает следующее:

T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
  ...

Поскольку T некоторый arbitary (кроме ограничения размера) тип, который мог иметь конструктора, я не могу сделать объединение T и Int. (Это позволяется только в C++ 0x только и еще даже не поддерживается GCC).

Есть ли какой-либо способ, которым я мог переписать вышеупомянутый псевдокод, чтобы сохранить функциональность и постараться не нарушать строгое правило искажения? Обратите внимание, что это - шаблон, я не могу управлять T или значение some_other_t; присвоение и последующее сравнение действительно происходят в шаблонном коде.

(Для записи вышеупомянутый код начал повреждаться на GCC 4.5 если T содержит любые битовые поля.)

8
задан curiousguy 16 July 2012 в 05:26
поделиться

5 ответов

static inline int is_T_0(const T *ob)
{
        int p;
        memcpy(&p, ob, sizeof(int));
        return p == 0;
}

void myfunc(void)
{
    T x = some_other_t;
    if (is_T_0(&x))
        ...

На моей системе GCC оптимизирует как is_T_0(), так и memcpy(), в результате чего в myfunc() остается всего несколько инструкций ассемблера.

1
ответ дан 6 December 2019 в 01:39
поделиться

Вы слышали о boost :: optional ?

Должен признать, я не понимаю, в чем заключается настоящая проблема ... но boost :: optional позволяет хранить по значению и все же знать, есть ли или не была инициализирована фактическая память. Я также допускаю строительство и разрушение на месте, так что я думаю, это может быть хорошо.

РЕДАКТИРОВАТЬ :

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

К сожалению, ваше решение содержит огромную проблему: оно неверно. Если когда-либо T каким-то образом может быть представлен битовой комбинацией null , то вы подумаете, что это унифицированная память.

Вам придется прибегнуть к добавлению хотя бы одного бита информации. На самом деле это немного, ведь это всего 3% прироста (33 бита на 4 байта).

Вы можете, например, использовать некоторый mimick boost :: optional , но в виде массива (чтобы избежать потери заполнения).

template <class T, size_t N>
class OptionalArray
{
public:


private:
  typedef unsigned char byte;

  byte mIndex[N/8+1];
  byte mData[sizeof(T)*N]; // note: alignment not considered
};

Тогда это так просто:

template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
  return mIndex[i/8] & (1 << (i%8));
}

template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
  assert(!this->null(i));
  return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}

примечание : Для простоты я не рассматривал вопрос согласования. Если вы не знаете о предмете, прочтите об этом, прежде чем возиться с памятью :)

1
ответ дан 6 December 2019 в 01:39
поделиться

Как насчет этого:

Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)

Это может быть не так эффективно, но должно избавить от предупреждения.


ДОБАВЛЕНИЕ #1:

Поскольку T ограничено тем же размером, что и Int, создайте себе фиктивное значение типа T с побитовым нулем и сравнивайте непосредственно с ним (вместо приведения и сравнения с Int(0)).

Если ваша программа однопоточная, вы можете получить что-то вроде этого:

template <typename T>
class Container
{
public:
    void foo(T val)
    {
        if (zero_ == val)
        {
            // Do something
        }
    }

private:
    struct Zero
    {
        Zero() {memset(&val, 0, sizeof(val));}
        bool operator==(const T& rhs) const {return val == rhs;}
        T val;
    };
    static Zero zero_;
};

Если она многопоточная, вы захотите избежать использования статического члена zeroo_, и пусть каждый экземпляр контейнера содержит свой собственный член zeroo_:

template <typename T>
class MTContainer
{
public:
    MTContainer() {memset(zero_, 0, sizeof(zero_));}

    void foo(T val)
    {
        if (val == zero_)
        {
            // Do something
        }
    }

private:
    T zero_;
};

ДОБАВЛЕНИЕ #2:

Позвольте мне сформулировать вышеприведенное дополнение другим, более простым способом:

// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));

T x = some_other_t;
if (x == zero)
1
ответ дан 6 December 2019 в 01:39
поделиться

Это похоже на взлом, но, видимо, я нашел решение: использовать volatile для приведения Int . По сути, сейчас я делаю следующее:

T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
  ...

Проблема с битовым полем T теперь исчезла. Тем не менее, я не очень доволен этим, поскольку volatile не очень четко определен в C ++ AFAIK ...

-2
ответ дан 6 December 2019 в 01:39
поделиться

Используйте 33-битный компьютер. ;-P

0
ответ дан 6 December 2019 в 01:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: