haker_fox 162 March 4 Posted March 4 · Report post Добрый день! Не могу решить задачу, если она решаемая. Chat GPT и Stack Overflow опрошены, но результаты мне не понравились. // Настроечная структура содержит настройки для вольтметра и амперметра struct Settings { VSettings voltmeter; ASettings ampermeter; // Типы VSettings и ASettings разные, не имеют ничего общего, но имеют одинаковые названия полей auto & get( int ch ) { // Хочу, чтобы этот метод возвращал разные структуры настройки в зависимости от величины аргумента if (ch < 3) return voltmeter; else return ampermeter; } }; // Использовать хочу так Settings s; s.get(chvalue).поле_структуры = некое_значение; Это возможно на Си++ 17?) Quote Share this post Link to post Share on other sites More sharing options...
makc 385 March 4 Posted March 4 · Report post Template не вариант? Quote Share this post Link to post Share on other sites More sharing options...
haker_fox 162 March 4 Posted March 4 · Report post 1 minute ago, makc said: Template не вариант? Вариант. Но я не могу сам написать даже по мотивам того, что предложили в интернете. Нет полностью подходящего примера. Сможете подсказать? Предлагается std::variant, но от требует проверки возвращаемого значения на принадлежность типу. А вариант с if constexpr меня не устраивает по причине переменного аргумента функции, который не известен на этапе компиляции. Quote Share this post Link to post Share on other sites More sharing options...
Anxigeros 6 March 4 Posted March 4 · Report post Опросил 3 нейронки, все выдали примерно одинаковый результат. Что не устраивает? Язык C++ - статически типизированный, т.е. все типы должны быть объявлены или вычислены на этапе компиляции. Если тип зависит от runtime-данных (как у вас, где значение `ch` может прилетать из космоса) - то это задача программиста императивно описать динамическую диспетчеризацию. 1. Если `ch` может быть вычислен в compile-time (например, перебор от 1 до 8), то `ch` переносится в шаблонный параметр (а цикл заменяется через `std::integer_sequence` с fold expression). 2. Если `ch` только в runtime, то явно пишем реализацию динамических типов. Вариант с `std::variant` и `std::visit` мне понравился - просто и понятно. Если же есть возможность управлять типами `VSettings` и `ASettings`, то наследование от общего родителя - самое оптимальное по потреблению ресурсов. Оффтопом: раньше работал с IAR EWARM 9.50, в котором была заявлена поддержка C++17. При этом в поставляемой стандартной библиотеке отсутствовал `std::apply` и еще несколько шаблонов. Quote Share this post Link to post Share on other sites More sharing options...
dxp 213 March 4 Posted March 4 · Report post 4 часа назад, haker_fox сказал: s.get(chvalue).поле_структуры = некое_значение; В рамках строгой статической типизации такое сделать вряд ли получится, ведь тут явно указаны все имена, а перегрузки функций по типу возвращаемого значения в С++ нет, да и не работало бы это, т.к. перегрузка разрешается на этапе компиляции, а у вас тут рантайм. Может, конечно, новомодные ++ что-то адекватное и предлагают, но мне видится тут один путь. Поскольку хочется полиморфизма -- получать разное поведение при одинаковых словах, и полиморфизм получается динамическим (т.е. на рантайме), то надо крутить в сторону виртуальных функций. Для того VSettings и ASettings должны быть потомками общего базового класса, имеющего виртуальные функции, возвращающие поля, а в потомках эти функции перепределить, чтобы каждая возвращала ссылку на собственное поле. Тогда должно получиться что-то вроде: s.get(chvalue)->set_<поле_структуры>() = некое_значение; Функция get() имеет тип возврата "указатель на базовый класс", а возвращает адрес на объект конкретного типа (V- или ASettings), а set_<поле_структуры>() -- это виртуальная функция, возвращающая ссылку на поле. Вроде бы не сильно громоздко и сложно, по эффективности тоже должно быть норм, рантайм-накладные в любом случае какие-то будут. Quote Share this post Link to post Share on other sites More sharing options...
haker_fox 162 March 4 Posted March 4 · Report post 7 minutes ago, Anxigeros said: Что не устраивает? Не устраивает отсутствие готового примера. Вы вот можете написать эту функцию? Я не могу. Я не очень хороший знаток Си++, чтобы сообразить, как это сделать по описаниям, которые вы привели. 8 minutes ago, Anxigeros said: то наследование от общего родителя - самое оптимальное по потреблению ресурсов. Так и сделано пока. Но вот возникло желание попробовать мощь ООП во всей красе. Тем более, она уже понадобилась. 5 minutes ago, dxp said: Вроде бы не сильно громоздко и сложно, по эффективности тоже должно быть норм, рантайм-накладные в любом случае какие-то будут. Уф, спасибо!))) Это уже выше моего понимания, но я попробую разрбраться. Если не смогу, то оставлю пока как есть: две структуры имеют общего родителя. 12 minutes ago, Anxigeros said: Если `ch` только в runtime, то явно пишем реализацию динамических типов. Вариант с `std::variant` и `std::visit` мне понравился - просто и понятно. Да, это мой вариант. Но реализации не понравились, как я отметил выше) Quote Share this post Link to post Share on other sites More sharing options...
novikovfb 40 March 4 Posted March 4 · Report post 39 minutes ago, haker_fox said: Но вот возникло желание попробовать мощь ООП во всей красе. Вы пытаетесь на С++ писать, как на Питоне. Вряд ли получится что-то хорошее. Quote Share this post Link to post Share on other sites More sharing options...
haker_fox 162 March 4 Posted March 4 · Report post 30 minutes ago, novikovfb said: Вы пытаетесь на С++ писать, как на Питоне. Вы вопрос прочитали? Я напомню: 5 hours ago, haker_fox said: Это возможно на Си++ 17?) И да, я пока не пытаюсь писать. Я в отличие от тех, кто голословно что-то говорит про Питон, хотя бы задаю вопросы. 36 minutes ago, novikovfb said: Вряд ли получится что-то хорошее. Вы -- эксперт? Тогда наверняка у вас есть какие-то предложения чуть более ёмкие и полезные, чем пространственные изречения. Или нет? Quote Share this post Link to post Share on other sites More sharing options...
novikovfb 40 March 4 Posted March 4 · Report post 38 minutes ago, haker_fox said: Вы -- эксперт? Тогда наверняка у вас есть какие-то предложения чуть более ёмкие и полезные, чем пространственные изречения. Или нет? Штаны можно надеть через голову, но вряд ли это понравится. Прочитайте, как внутри С++ устроены и работают классы, наследование, определение типов, станет понятно, что это - совсем не Питон, где всё через строковые имена. Именно поэтому код на С++ работает гораздо быстрее (ему не нужно для каждого чиха сравнивать строки имен типов, строки имен полей и методов и т.д.), но поэтому сделать динамическое определение типа гораздо сложнее, чем в Питоне просто спросить у объекта, какого он класса. Судя по вопросу, задача поставлена как-то странно. Как уже писали, для С++ у такой "фабрики классов" придется делать общего предка и наследование от него VSettings и ASettings, а также возвращать из функции не объект, а указатель на созданный в куче экземпляр нужного класса. Quote Share this post Link to post Share on other sites More sharing options...
x893 85 March 4 Posted March 4 · Report post #include <stdio.h> struct XSettings { }; struct VSettings : XSettings { int vol; }; struct ASettings : XSettings { int amp; }; struct Settings { VSettings voltmeter; ASettings ampermeter; XSettings & get( int ch ) { // Хочу, чтобы этот метод возвращал разные структуры настройки в зависимости от величины аргумента if (ch < 3) return voltmeter; else return ampermeter; } }; int main() { struct Settings settings = { .voltmeter = { .vol = 5}, .ampermeter = { .amp = 6}, }; printf("\n"); printf(" vol: %d\n", ((VSettings &)settings.get(1)).vol); printf(" amp: %d\n", ((ASettings &)settings.get(3)).amp); return 0; } 1 Quote Share this post Link to post Share on other sites More sharing options...
novikovfb 40 March 4 Posted March 4 · Report post 50 minutes ago, x893 said: struct Settings { VSettings voltmeter; ASettings ampermeter; XSettings & get( int ch ) { // Хочу, чтобы этот метод возвращал разные структуры настройки в зависимости от величины аргумента if (ch < 3) return voltmeter; else return ampermeter; } }; только после получения этого XSettings уже нет информации, это напряжение или ток. Т.е. printf(" vol: %d\n", ((VSettings &)settings.get(3)).vol); компилятор проглотит и выдаст какое-то значение, которое вряд ли понравится автору Quote Share this post Link to post Share on other sites More sharing options...
x893 85 March 4 Posted March 4 · Report post 6 minutes ago, novikovfb said: только после получения этого XSettings уже нет информации, это напряжение или ток. Т.е. printf(" vol: %d\n", ((VSettings &)settings.get(3)).vol); компилятор проглотит и выдаст какое-то значение, которое вряд ли понравится автору В черном квадрате то, что выведет. Quote Share this post Link to post Share on other sites More sharing options...
novikovfb 40 March 4 Posted March 4 · Report post 5 minutes ago, x893 said: В черном квадрате то, что выведет. Я немного изменил Ваш текст в своем примере, смотрите внимательнее. Поэтому эта строка выдаст: vol: 6 сведений о фактическом типе возвращаемого значения нет, возьмет поле по смещению, соответствующему приведенному типу 1 Quote Share this post Link to post Share on other sites More sharing options...
x893 85 March 4 Posted March 4 · Report post 1 hour ago, novikovfb said: Я немного изменил Ваш текст в своем примере, смотрите внимательнее. Поэтому эта строка выдаст: vol: 6 сведений о фактическом типе возвращаемого значения нет, возьмет поле по смещению, соответствующему приведенному типу Меняйте, мне фиолетово. 1 hour ago, novikovfb said: только после получения этого XSettings уже нет информации, это напряжение или ток. Т.е. printf(" vol: %d\n", ((VSettings &)settings.get(3)).vol); компилятор проглотит и выдаст какое-то значение, которое вряд ли понравится автору Если знаний нет, то и результат будет какой угодно. Quote Share this post Link to post Share on other sites More sharing options...
Anxigeros 6 March 4 Posted March 4 · Report post 11 часов назад, haker_fox сказал: Предлагается std::variant, но от требует проверки возвращаемого значения на принадлежность типу. Все так: в одном месте вы явно "запаковываете" информацию о типе (std::variant), а в другом месте - "распаковываете" (диспатчите, std::visit). И все руками, разумеется. Спойлер struct Settings { VSettings voltmeter; ASettings ampermeter; std::variant<VSettings*, ASettings*> get(int ch) { if (ch < 3) return &voltmeter; else return &ermeter; } }; Settings s{}; int main() { std::array channels = { 2, 4 }; for (const auto chvalue : channels) { std::visit([=](auto* settings) { settings->digits = 10 + chvalue; }, s.get(chvalue)); } std::cout << "VSettings::digits = " << s.voltmeter.digits << '\n'; std::cout << "ASettings::digits = " << s.ampermeter.digits << '\n'; } Есть еще такой вариант: вместо возврата типа-суммы, создаем коллбэк-функции под каждый тип (т.е. одну шаблонную функцию, которую потом инстанируем). Спойлер struct Settings { VSettings voltmeter; ASettings ampermeter; template<typename F> void apply(int ch, F&& func) { if (ch < 3) func(voltmeter); else func(ampermeter); } }; Settings s{}; int main() { std::array channels = { 2, 4 }; for (const auto chvalue : channels) { s.apply(chvalue, [=](auto& settings) { settings.digits = 10 + chvalue; }); } std::cout << "VSettings::digits = " << s.voltmeter.digits << '\n'; std::cout << "ASettings::digits = " << s.ampermeter.digits << '\n'; } 11 часов назад, haker_fox сказал: Но вот возникло желание попробовать мощь ООП во всей красе. Если именно "мощь ООП", то только через интерфейсы! Спойлер struct ISettings { virtual ~ISettings() = default; virtual int& digits() = 0; virtual float& value() = 0; }; struct VSettings : public ISettings { float voltage; int my_digits; int& digits() override { return my_digits; } float& value() override { return voltage; } }; struct ASettings : public ISettings { float amperage; int my_digits; int& digits() override { return my_digits; } float& value() override { return amperage; } }; struct Settings { VSettings voltmeter; ASettings ampermeter; ISettings& get(int ch) { if (ch < 3) return voltmeter; else return ampermeter; } }; Settings s{}; int main() { std::array channels = { 2, 4 }; for (const auto chvalue : channels) { s.get(chvalue).digits() = 10 + chvalue; s.get(chvalue).value() = 30 + chvalue * 3; } std::cout << "VSettings::digits = " << s.voltmeter.my_digits << '\n'; std::cout << "VSettings::voltage = " << s.voltmeter.voltage << '\n'; std::cout << "ASettings::digits = " << s.ampermeter.my_digits << '\n'; std::cout << "ASettings::amperage = " << s.ampermeter.amperage << '\n'; } 3 1 Quote Share this post Link to post Share on other sites More sharing options...