#c #struct #types
#c #структура #типы
Вопрос:
У меня есть структура указателей, где указатели имеют произвольный тип, но известны во время компиляции.
struct ptrs
{
int* a;
const char** b;
int* c;
float* d;
};
Учитывая, что все они являются указателями, все они будут одинаковой длины. Есть ли способ, которым я могу получить доступ к n-му элементу с информацией о типе.
Я мог бы преобразовать структуру в массив указателей void, но тогда я бы потерял представление о том, на какой тип я смотрю, аналогично с использованием объединения.
Вся кодовая база находится на c 20, поэтому мне не нужно беспокоиться о поддержке C.
Редактировать: я хочу это для следующего использования. Я создам переменный макрос для преобразования произвольного количества входных данных в массив указателей на then . Это будет передано функции для обработки, которая будет обрабатывать их в произвольном порядке в соответствии с командной строкой. Например, «return $var1 $ var2». Для обработки требуется произвольный доступ к элементам один или несколько раз, следовательно, «индексирование» структуры. Однако для поддержки арифметики тип должен быть известен.
Комментарии:
1. Какова конечная цель этого упражнения? Чего вы на самом деле пытаетесь достичь? Ваш вопрос звучит как проблема XY ; сам по себе он не имеет особого смысла.
2. C не поддерживает отражение.
3. Хотите верьте, хотите нет, на самом деле это проще гарантировать с помощью C.
4. В какой форме вы ожидаете получить тип, если n известно только во время выполнения, как компилятор может дать вам это?
5.
std::tuple
Может помочь использование (непосредственно через преобразование).
Ответ №1:
Да, это можно сделать. Часть кода немного сложно написать, но 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 autoamp; get(autoamp; your_struct)
{
autoamp; [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>
or get<2>
(или что угодно), но не оба, так что это чисто ограниченное доказательство концепции, а не практическая реализация (вообще).
Хотя вот рабочая демонстрационная программа:
#include <cstddef>
#include <iostream>
struct ptrs {
int* a;
const char** b;
int* c;
float* d;
};
template <std::size_t index>
constexpr autoamp; get(autoamp; your_struct)
{
autoamp; [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"
}
Комментарии:
1. хотя это интересно, я не уверен, как этот подход был отмечен как ответ, поскольку он требует знания времени компиляции
n
. вопрос, как выяснено, требует доступа к n, определяемому строковым вводом во время выполнения
Ответ №2:
Это единственный способ, без создания множества механизмов метаданных.
void* get(ptrs constamp; data, int n)
{
// TODO: add checks
return ((void**)amp;data)[n];
}
Следует отметить, что стандарт не гарантирует, что все указатели данных имеют одинаковый размер. Однако на большинстве архитектур они есть. Поэтому обратитесь к документации вашего компилятора или используйте a static_assert
для подтверждения.
Комментарии:
1. Это UB типа «Вероятно, работает». Не забывайте следить за скрытыми метаданными в более сложных структурах данных.