Как изменить тип данных вектора C ++ "в виде соединения" - PullRequest
3 голосов
/ 15 января 2020

Я хотел бы знать, возможно ли в C ++ изменить тип std :: vector, уже заполненного значениями, именно так, как работает объединение, то есть:

  • без изменения одного бита двоичного содержимого
  • без вычислений приведения типов (без математических операций)
  • просто переосмысление содержимого двоичных данных с использованием нового типа (например, uint16 или float32) без любая копия или перераспределение памяти (как я хотел бы использовать векторы размером несколько гигабайт)

Например, у меня есть вектор, заполненный 20 значениями: 0x00, 0x01, 0x02, 0x03 .. и я хочу интерпретировать его как вектор из 10 значений с одинаковым общим двоичным содержимым: 0x0001, 0x0203 (в зависимости от соглашения с прямым и прямым порядком байтов)

Самое близкое, что я мог сделать, это :

vector<uint8_t> test8(20);
uint16_t* pv16 = (uint16_t*) (&test8[0]);
vector<uint16_t> test16(pv16, pv16+10);

Результат - именно то, что я хочу, за исключением того, что он делает копию всех данных, тогда как я хотел бы использовать существующие данные.

Буду признателен за любую помощь на этот предмет.

Большое спасибо за ваш ответ.

Ответы [ 3 ]

3 голосов
/ 15 января 2020

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

Пожалуйста, ознакомьтесь также с указанием типа и неопределенным поведением в C ++, так как это довольно тонкий топи c. См https://blog.regehr.org/archives/959

#include <type_traits>
#include <cstring>
#include <cstdint>
#include <vector>

template <typename To>
class punned_view
{
    static_assert(std::is_trivial<To>::value);
    const char* begin_;
    const char* end_;
public:
    template <typename From>
    punned_view(From* begin, From* end)
        : begin_{reinterpret_cast<const char*>(begin)}
        , end_{reinterpret_cast<const char*>(end)}
    {
        static_assert(sizeof(To) >= sizeof(From)); // exercise to make it work with smaller types too
        static_assert(std::is_trivial<From>::value);
        // add checks that size is a multiple of To here
    }

    std::size_t size() const noexcept
    {
        return (end_ - begin_) / sizeof(To);
    }

    class const_iterator
    {
        const char* current_;
    public:
        const_iterator(const char* current)
            : current_{current}
        { }

        const_iterator& operator++() noexcept
        {
            current_ += sizeof(To);
            return *this;
        }
        To operator*() const noexcept
        { 
            To result;
            // only legal way to type pun in C++
            std::memcpy(&result, current_, sizeof(result));
            return result;
        }
        bool operator != (const_iterator other) const noexcept
        {
            return current_ != other.current_;
        }
    };

    const_iterator begin() const noexcept { return {begin_}; }
    const_iterator end() const noexcept { return {end_}; }
};

uint16_t sum_example(const std::vector<uint8_t>& vec)
{
    punned_view<uint16_t> view{vec.data(), vec.data() + vec.size()};

    uint16_t sum = 0;
    for (uint16_t v : view)
        sum += v;

    return sum;
}
0 голосов
/ 16 января 2020

и спасибо за все ваши быстрые и подробные ответы. Я был приятно удивлен, так как в последний раз, когда я использовал форум (Eclipse), я помню, что получал ровно ноль ответов после целого месяца ...

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

Короче говоря, я действительно хочу прочитать содержимое изображения tiff (спутниковое изображение только с полутоновыми значениями, без RGB или любой цветовой комбинации), используя gdal в C ++, а затем выполнить несколько простых операции, некоторые из них как basi c как получение правильных значений пикселей. Звучит просто как ад, не так ли? Теперь в реальной жизни все становится кошмаром, когда используется gdal (который столь же мощен, как crypti c) и НЕ знает заранее фактический тип данных пикселей (который может быть в основном любым типом int или с плавающей точкой с любой точностью). Насколько я мог понять с помощью учебных пособий, примеров и форума, gdal предлагает мне только 2 (едва удовлетворительных) способа чтения содержимого изображения tiff:

1) либо я точно знаю тип данных пикселей моего изображения (ex int16), и я должен где-то жестко закодировать его, что я не могу себе позволить (и шаблоны здесь не помогут, так как в определенный момент мне приходится хранить содержимое моего изображения в переменной, а это значит, что я должен знать его точное type).

2) или я могу прочитать изображение любого типа данных пикселей, но используя автоматическое преобразование c в заданный целевой тип (ex float64, чтобы охватить все возможные диапазоны значений). Звучит удобно и легко, но недостатком является то, что это систематическое c преобразование является потенциально огромной тратой времени и памяти (вспомните uint8 в исходном массиве, преобразованном в float64 в целевом массиве!). Безумный вариант для меня, так как я обычно работаю с очень большими изображениями (например, с несколькими гигапикселями!)

3) Я сам определился с некрасивым / неуклюжим альтернативным решением, где я позволил gdal загрузить изображение содержимое в виде «необработанного двоичного» содержимого (официально это массив байтов), а затем, в конце концов, пытается прочитать его обратно, интерпретируя его в соответствии с реальным типом данных (который gdal может сообщить мне позже). Хорошей стороной является то, что точное двоичное содержимое изображения загружается без какого-либо преобразования, поэтому лучшая скорость и использование памяти. Недостатком является то, что я в конечном итоге пытаюсь поиграть с этими двоичными данными, чтобы правильно их интерпретировать, избегая любых копий или математических операций.

Так вот что привело меня в эту неуклюжую попытку «на месте -интерпретация »моих данных, или как там их собственное имя, просто потому, что я думал, что это будет очень простой и последний шаг к выполнению работы, но я могу ошибаться, и я мог пропустить более простые / более чистые решения (на самом деле У меня есть sh У меня есть!).

Несколько заключительных мыслей о том, чтобы "де-Y" ответить на мой вопрос XY !!!

_ использование библиотеки gdal представляется здесь почти обязательным, поскольку Насколько я знаю, это единственная библиотека, которая может правильно обрабатывать изображения, с которыми я имею дело, то есть многополосные TIFF-изображения (другие библиотеки обычно всегда рассматривают 3 полосы и слепо интерпретируют их как компоненты цвета RGB, что совершенно не то, что Я хочу здесь).

_ также я быстро попробовал с gdal для python, но обработал гигапиксельное большое изображение s в python звучит определенно как неправильный выбор. Более того, моим следующим шагом должно быть создание базового c интерактивного средства просмотра изображений (возможно, с использованием Qt), поэтому скорость выполнения действительно имеет значение.

_ Я много упоминал с использованием std :: vector, потому что думал, что это будет было бы легче играть, но, вероятно, массив старой школы C справился бы с работой.

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

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

Еще раз спасибо.

0 голосов
/ 15 января 2020

Чтобы получить данные в качестве другого типа, этого можно достичь с помощью некоторых указателей и кастомов c. В C ++ 11 и более поздних версиях вы можете получить указатель на необработанные данные в виде std::vector http://www.cplusplus.com/reference/vector/vector/data/

void* p;
uint16_t* p2;

std::vector<uint32_t> myvector;
myvector.push_back(0x12345678);
myvector.push_back(400);

p=myvector.data();
p2 = (uint16_t*)p;

for (size_t i = 0; i < 2*myvector.size(); i++) {
    std::cout << *p2++ <<",";
}

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

...