Есть ли способ получить доступ к n-му элементу структуры?

У меня есть структура указателей, где указатели имеют произвольный тип, но известны во время компиляции.

struct ptrs
{
    int* a;
    const char** b;
    int* c;
    float* d;
};

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

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

Вся кодовая база находится на С++ 20, поэтому мне не нужно беспокоиться о поддержке c.

Обновлено: я хочу это для следующего использования. Я создам вариативный макрос для преобразования произвольного количества входных данных в массив указателей на then. Это будет передано функции для обработки, которая будет обрабатывать их в произвольном порядке в соответствии с командной строкой. НАПРИМЕР. "вернуть $var1 + $var2". Обработка требует случайного доступа к элементам один или несколько раз, следовательно, "индексация" структуры. Однако для поддержки арифметики необходимо знать тип.

Какова конечная цель этого упражнения? Чего вы действительно пытаетесь достичь? Ваш вопрос звучит как задача XY; это не имеет особого смысла само по себе.

Igor Tandetnik 25.12.2020 05:41

С++ не поддерживает отражение.

DarthQuack 25.12.2020 05:47

Хотите верьте, хотите нет, но это на самом деле легче гарантировать с помощью C.

user4581301 25.12.2020 05:56

В какой форме вы ожидаете получить тип, если n известно только во время выполнения, как компилятор может дать вам это?

Patrick Parker 25.12.2020 05:57

Использование std::tuple (непосредственно или через преобразование) может помочь.

Jarod42 25.12.2020 11:07
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
1 006
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это единственный способ, не придумывая кучу метаданных.

void* get(ptrs const& data, int n)
{
    // TODO: add checks
    return ((void**)&data)[n];
}

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

Это УБ типа "Наверное рабочий". Не забывайте следить за скрытыми метаданными в более сложных структурах данных.

user4581301 25.12.2020 05:55
Ответ принят как подходящий

Да, это может быть сделано. Часть кода написать немного сложно, но 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, определяемому строковым вводом во время выполнения

Patrick Parker 25.12.2020 08:16

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