Укажите тип указателя C++ во время выполнения (универсальный указатель)

Я разрабатываю код моделирования, полностью написанный на C++, где мы используем двойную точность для всех вычислений. Теперь я хочу, чтобы пользователь мог настроить его на использование одинарной точности. Проблема в том, что все наши матрицы определены как двойные, поэтому мне интересно, могу ли я иметь общий указатель и указывать тип во время выполнения. Например, предположим, что наш код выглядит так:

double *mat = nullptr;
mat = new double[some_size];
// Some operations...
delete[] mat;

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

void *ptr = nullptr;                                                                                                                                                                                                                    
ptr = new complex<double>[10];

for (auto i = 0; i < 10; ++i) {
  ptr[i].real(1.33445);
  ptr[i].imag(443.223);
}

delete[] ptr;

В этом случае я получаю следующую ошибку:

error: subscript of pointer to incomplete type 'void'

Также я получаю предупреждение о том, что указатель void нельзя удалить.

Теперь мой вопрос:

Есть ли какой-либо возможный способ (похожий на указатель void) получить общий указатель, а затем правильно привести его к определенному типу во время выполнения?

А volid* это просто: void ничего не значит, поэтому его нельзя использовать для указания правильного типа. И если бы вы использовали указатель на single для доступа к матрице double, вы бы просто получили мусор, поскольку все смещения были бы неверными.

OldBoy 20.07.2024 10:51

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

Streve Ford 20.07.2024 10:58

Использование шаблонов для типа данных является очевидным решением.

Richard Critten 20.07.2024 11:13

Не отмечайте C для вопросов C++.

Eric Postpischil 20.07.2024 12:17

Шаблоны C++ обсуждаются в любой хорошей книге и/или учебном пособии по C++. Как такие символы, как std::vector, std::list и т. д., работают с разными типами? Даже глядя на свой код, вы видите complex<double> -- как это работает, где вы указываете, что complex использует double? Шаблоны.

PaulMcKenzie 20.07.2024 12:20

@EricPostpischil Вопрос справедлив и для языка C.

Dan 20.07.2024 12:21

@Dan Вопрос справедлив и для языка C. В коде вашего вопроса используются new и delete, поэтому это не C. Во-вторых, в C++ есть способы решения этой проблемы, которых нет у C.

PaulMcKenzie 20.07.2024 12:22

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

n. m. could be an AI 20.07.2024 12:31

Короткий ответ: нет. Последствием сохранения непустого указателя (в первом примере double *) в void * является потеря всей информации о типе. Более того, чтобы использовать void * так, как будто он указывает на что-то другое, необходимо преобразовать указатель обратно в правильный тип, например. double *dptr = (double *)ptr; *dptr = 42.0; правильно в вашем первом случае, но float *fptr = (float *)ptr; *fptr = 42.0f; (или преобразование в ЛЮБОЙ тип, кроме double * и разыменования) дает НЕОПРЕДЕЛЕННОЕ поведение. По сути, это одно и то же как в C, так и в C++.

Peter 20.07.2024 12:39

По моему мнению, было бы более выгодно шаблонизировать ваш матричный код, указав T в качестве параметра шаблона, а затем разработать способ выбора T во время выполнения и работы оттуда (существуют различные способы сделать это). Использование void * и попытка преодолеть все эти препятствия, упомянутые другими (например, неопределенное поведение), — это проигрышная попытка.

PaulMcKenzie 20.07.2024 12:50

@Dan: Даже если бы вопрос был действительным для C, это не значит, что ответы будут такими же. В ответах могут использоваться специфические особенности языка. И это проблема для Stack Overflow, потому что он здесь не только для вас. Он задуман как надежное хранилище вопросов и ответов для других людей в будущем. Кто-то будет искать Stack Overflow, используя тег C, и мы не хотим, чтобы его результаты поиска были забиты бесполезными ответами на C++. Политика в отношении тегов C и C++ заключается в том, чтобы не объединять их без каких-либо причин, таких как вопрос о взаимодействии или различиях между языками.

Eric Postpischil 20.07.2024 13:17

Ваши «некоторые операции» вашего кода изменяются, если ваш тип — double* вместо complex<double>*, поскольку double не имеет .real и .imag. Программирование шаблонов может дать вам лишь определенный результат, но тогда различия в типах придется обрабатывать либо абстракцией типов данных, либо специализацией шаблонов.

Eljay 20.07.2024 13:36

подойдет ли что-нибудь по номеру std::variant<double, float>? (С++17....)

ticktalk 20.07.2024 20:01

Спасибо всем, кто потратил свое драгоценное время на эту проблему. Решение, опубликованное @Chukwujiobi Canon, решило мою проблему. Итак, я создам указатель void, а затем приведу его к правильному типу всякий раз, когда мне нужно будет передать матрицу соответствующей функции в LAPACK для правильного типа.

Dan 20.07.2024 20:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
14
130
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

void* не предназначен для такого использования. Единственное хорошее применение void* — это низкоуровневый код, где нам нужно передать адрес ячейки памяти.

Если вам необходимо получить доступ к объекту, расположенному в этом месте памяти, как при использовании оператора индекса, вы должны явно преобразовать это void* в T*, где T — полный тип.

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

void* ptr = nullptr;                                                                                                                                                                                                                    
ptr = new complex<double>[10];

for (auto i = 0; i < 10; ++i) {
  static_cast<complex<double>*>(ptr)[i].real(1.33445);
  static_cast<complex<double>*>(ptr)[i].imag(443.223);
}

delete[] static_cast<complex<double>*>(ptr);

Большое спасибо за вашу помощь! Этот код решил мою проблему. Мне нужно передать указатель void в функции LAPACK и BLAS, и с помощью вашего решения я могу привести указатель в соответствии с функцией.

Dan 20.07.2024 20:27

Другие вопросы по теме