У меня есть структура указателей, где указатели имеют произвольный тип, но известны во время компиляции.
struct ptrs
{
int* a;
const char** b;
int* c;
float* d;
};
Учитывая, что все они являются указателями, все они будут одинаковой длины. Есть ли способ получить доступ к n-му элементу с информацией о типе.
Я мог бы преобразовать структуру в массив указателей void, но тогда я потерял бы информацию о том, на какой тип я смотрю, точно так же, как и при использовании объединения.
Вся кодовая база находится на С++ 20, поэтому мне не нужно беспокоиться о поддержке c.
Обновлено: я хочу это для следующего использования. Я создам вариативный макрос для преобразования произвольного количества входных данных в массив указателей на then. Это будет передано функции для обработки, которая будет обрабатывать их в произвольном порядке в соответствии с командной строкой. НАПРИМЕР. "вернуть $var1 + $var2". Обработка требует случайного доступа к элементам один или несколько раз, следовательно, "индексация" структуры. Однако для поддержки арифметики необходимо знать тип.
С++ не поддерживает отражение.
Хотите верьте, хотите нет, но это на самом деле легче гарантировать с помощью C.
В какой форме вы ожидаете получить тип, если n известно только во время выполнения, как компилятор может дать вам это?
Связанный: Является ли структура строк такой же, как массив строк?.
Использование std::tuple
(непосредственно или через преобразование) может помочь.
Это единственный способ, не придумывая кучу метаданных.
void* get(ptrs const& data, int n)
{
// TODO: add checks
return ((void**)&data)[n];
}
Следует отметить, что стандарт не гарантирует, что все указатели данных имеют одинаковый размер. Однако на большинстве архитектур они есть. Поэтому обратитесь к документации вашего компилятора или используйте static_assert
для подтверждения.
Это УБ типа "Наверное рабочий". Не забывайте следить за скрытыми метаданными в более сложных структурах данных.
Да, это может быть сделано. Часть кода написать немного сложно, но Boost PFR уже проделал тяжелую работу, поэтому вы можете сделать что-то вроде:
ptrs p;
float * f = boost::pfr::get<3>(p); // get the `float *` member
*f = 123.4f; // and write via the pointer
[Обратите внимание, что я разбил это только на две строки, чтобы я мог прокомментировать каждый шаг — что-то вроде *boost::pfr::get<3>(p) = 123.4f;
тоже должно подойти.]
Для всех, кому небезразлично, основная идея того, как это сделать, довольно проста — только детали становятся хитрыми.
Основной подход заключается в использовании структурированного связывания для получения интересующего вас элемента. Хитрость возникает из нескольких источников: вы не знаете, с каким типом (типами) вы имеете дело, и типы различаются, поэтому это не так просто, как создание массива элементов и индексирование в массив.
Но все же, немного упростив, мы можем сделать что-то вроде этого:
template <std::size_t index>
constexpr auto& get(auto& your_struct)
{
auto& [zero, one, two, three] = your_struct;
if constexpr (index == 0)
return zero;
if constexpr (index == 1)
return one;
if constexpr (index == 2)
return two;
return three;
}
Я повторю: я сильно упрощаю (например, я только что использовал фиксированное количество членов), но это, по крайней мере, общая идея — использовать структурированное связывание для захвата членов и условие constexpr для выбора. какой из них вас интересует, с параметрами шаблона (в виде auto
) практически для всех частей, поэтому вы можете иметь дело с любыми типами, которые там есть.
О, есть еще одна деталь, с которой немного сложно разобраться: в нынешнем виде эта реализация может работать только для одного типа. То есть, если вы попытаетесь использовать get<0>
и get<3>
, вы получите ошибку о несоответствии в выведенном возвращаемом типе. Итак, на данный момент вы можете использовать get<0>
или get<2>
(или что-то еще), но не оба, так что это чисто ограниченное доказательство концепции, а не практическая реализация (вообще).
Вот рабочая демонстрационная программа:
#include <cstddef>
#include <iostream>
struct ptrs {
int* a;
const char** b;
int* c;
float* d;
};
template <std::size_t index>
constexpr auto& get(auto& your_struct)
{
auto& [zero, one, two, three] = your_struct;
if constexpr (index == 0)
return zero;
if constexpr (index == 1)
return one;
if constexpr (index == 2)
return two;
return three;
}
int main() {
ptrs p;
p.d = new float; // `get<3>(p) = new float;` works fine too.
*get<3>(p) = 123.4;
// show we're accessing the same data either way:
std::cout << *get<3>(p) << "\t" << *(p.d) << "\n";
// type information is retained. For example, if we try to do this:
// get<3>(p) = new int; // <-- note int instead of float
// g++ 10 gives the error: "cannot convert ‘int*’ to ‘float*’ in assignment"
}
хотя интересно, я не уверен, как этот подход был помечен как ответ, поскольку он требует знания времени компиляции n
. вопрос, как выяснилось, требует доступа к n, определяемому строковым вводом во время выполнения
Какова конечная цель этого упражнения? Чего вы действительно пытаетесь достичь? Ваш вопрос звучит как задача XY; это не имеет особого смысла само по себе.