Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.236.86.184] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Добрый день.
Попалась в справочном материале непонятная мне конструкция. Компилируется, но что это и как такое может быть не понимаю. struct S { int data; S():data(33){} }; int S::* f; // ?????? поисковики выпинывают на указатели, но f не указатель. что это? |
Сообщ.
#2
,
|
|
|
Указатель на элемент класса S типа int.
Добавлено f = &S::data; S x, y, *z = new S; /* ... */ x.*f = y.*f + z->*f; /* ... */ delete z; Добавлено В целом указатели на данные класса просто замещают тобой макрос offsetof() из C, при этом делая операции типобезопасными и более удобными. Но чаще встречаются указатели на методы класса, и они куда более мощная штука. Особенно если вспомнить, что методы бывают виртуальными, а классы виртуально наследуемыми. P.S. И да, они не указатели в том смысле, что не совместимы с простыми указателями на данные или функции. Даже с void*. И даже с указателями на такие же элементы другого класса, если только эти классы не связаны единой иерархией наследования. И над ними нет адресной арифметики. И sizeof() от них бывает довольно странный. |
Сообщ.
#3
,
|
|
|
Интересная конструкция, тоже никогда не встречал ее упоминаине
Цитата Qraizer @ В целом указатели на данные класса просто замещают тобой макрос offsetof() из C, при этом делая операции типобезопасными и более удобными. Где про это можно почитать? У меня есть код с offsetof и этот код мне не нравится: Скрытый текст struct address_map { data Data; uint8_t Temperature; // -75...125 deg.C, ~0.8 deg.C/LSB uint8_t TU : 2; // time unit of the synchronization events. Only 2 bits are used, // and the corresponding time units are 2^(-11), 2^(-12), 2^(-13), // and 2^(-14) of the TPH, respectively, for the TU value of 00, 01, 10, and 11. // Alternatively, this byte can be defined by I3C CCC command, with the defining // byte of 0x9F, followed by one byte of data. uint8_t reserved1[11]; status Status; uint8_t ODR; // This byte defines the frequency of the continuous-mode measurements. ODR can // be defined by either writing the number into this register, or using SETXTIME // CCC command, followed by defining byte of 0x8F. In order to enter the continuous // mode, this byte should not be zero. The configurable ODR is 1 to 255, with // increment of 1. The 1000 Hz ODR is available by writing 255 into this byte and // setting hpower to 1 struct control { core::control::reg0 Reg0; core::control::reg1 Reg1; core::control::reg2 Reg2; } Control; uint8_t Selftest_threshold[MAX_AXIS]; uint8_t reserved2[6]; uint8_t Selftest_set_signal[MAX_AXIS]; uint8_t reserved3[15]; uint8_t Product_ID; } __attribute__((__packed__)); ... #include <stddef.h> // offsetof() #define REG_OFFSET(reg) offsetof(core::address_map, reg) bool chip::detect() { using namespace core; using namespace control; debug_header("Product ID read"); uint8_t Product_id; if(!Transport.receive(Bus_address, REG_OFFSET(Product_ID), Product_id)) return disconnected(); if(Product_id != core::PRODUCT_ID) return failed(); State = OK; return debug_ok(); } Возможно, использование такого указателя поможет мне облагородить мой код. Цитата Qraizer @ Хм это уже вселяет пессимизм. С этими указателями вообще возможно сделать хоть что-то, кроме присваивания и разыменования? P.S. И да, они не указатели в том смысле, что не совместимы с простыми указателями на данные или функции. Даже с void*. |
Сообщ.
#4
,
|
|
|
Цитата Dushevny @ Так а что из примера непонятно? Указатели хранят инфу об элементе класса, абстрагируясь от конкретного экземпляра. Сами по себе они никуда не указывают, и польза от них только при их применении к конкретному экземпляру. Даже разыменование такого указателя делается специальными операторами, содержащим конкретный this, а не обычной *, которая этого this предоставить не может. Разные экземпляры класса – разные результаты применения .* или ->* к указателю. Конечно, такие указатели обязаны ещё корректно учитывать наследование.Возможно, использование такого указателя поможет мне облагородить мой код. Цитата Dushevny @ Ещё сравнения. А к чему там адресная арифметика? Что, хотя бы теоретически, могло бы быть результатом ++ к указателю? Обычно указатели на элементы классов используются тогда же, когда и обычные указатели: когда нет возможности или желания жёстко задавать имя подуказываемого объекта. Но обычные объекты могут быть объединены в массивы, а как быть с указателями на поля? К слову, над указателями на функции тоже нет адресной арифметики. Почему? За ненадобностью. Хм это уже вселяет пессимизм. С этими указателями вообще возможно сделать хоть что-то, кроме присваивания и разыменования? |
Сообщ.
#5
,
|
|
|
Цитата Qraizer @ Как именно заменить им offsetof(). Я привел пример своего кода, где offsetof() используется для получения адреса регистра в адресном пространстве микросхемы. Код работает, но автодополнение эклипсы при написании этого кода работать отказывается. Как там можно заменить offsetof() на такой указатель? Так а что из примера непонятно? |
Сообщ.
#6
,
|
|
|
Та как обычно, ставишь тип данных. Просто добавляешь к * область видимости.
uint8_t core::address_map::* ptr2ID = &core::address_map::Product_ID; core::address_map cpu; cpu.*ptr2ID Но что-то мне подсказывает, что между экземплярами core и core::address_map не так всё просто. Добавлено И вообще, почему-то мне кажется, что cpu – это синглон, а в этом случае тебе не нужен ни указатель на поле структуры, ни offsetof(). Просто берёшь адрес поля экземпляра и получаешь обычный поинтер. Добавлено Transport.receive(&cpu.Product_ID, Product_id); |
Сообщ.
#7
,
|
|
|
Цитата Qraizer @ core -это namespace. Экземпляра core::address_map тоже не существует, используется только смещение членов core::address_map от ее начала, это смещение и добывается при помощи offsetof(). Но что-то мне подсказывает, что между экземплярами core и core::address_map не так всё просто. Прикреплённая картинка
|
Сообщ.
#8
,
|
|
|
Ну так если тебе нужно смещение, то offsetof() тебе и нужен. Указатели на элементы класса нужны как раз для того, чтобы не заморачиваться внутренней структурой класса и при этом чётко следить за типами.
Если так уж хочется притянуть сюда Плюсовые фичи, то нужно менять интерфейс с железом. К примеру: /* linker script */ mcu_mbar : origin = 0x80000000, length = 0x00008000 MBAR = ADDR(mcu_mbar); struct MemAddr { union { unsigned int CS0STR; unsigned int CS0STP; unsigned int CS1STR; /* ... */ unsigned int CS7STP; }; unsigned char dummy[0x100]; }; struct MemCntr { union { unsigned int SDRAMMode; unsigned int SDRAMCtrl; unsigned int Config1; unsigned int Config2; }; unsigned char dummy[0x100]; }; struct CDM { union { unsigned int CDM_JTAG_ID; /* ... */ unsigned int CDM_PSC6CCR; }; unsigned char dummy[0x100]; }; /* ... */ struct RegSpace { MemAddr systemSpace; MemCntr SDRAM_control; CDM clockModule; /* ... */ }; extern unsigned char MBAR[]; volatile RegSpace ®Map = *reinterpret_cast<RegSpace*>(MBAR); Добавлено Ну и насобачить чё-нить: template <typename CpuArea, typename RegType, typename T> RegType out_reg(RegType CpuArea::* regPtr, T value, volatile CpuArea& cpu) { return cpu.*regPtr = value; } template <typename CpuArea, typename RegType> RegType in_reg(RegType CpuArea::* regPtr, volatile CpuArea& cpu) { return cpu.*regPtr; } template <typename CpuArea, typename RegType> RegType in_reg(RegType CpuArea::* regPtr, CpuArea RegSpace::* unit) { return regMap.*unit.*regPtr; } Юзать как так: auto clck = &RegSpace::clockModule; out_reg(&CDM::CDM_PSC6CCR, 7, regMap.*clck); std::cout << in_reg(&MemCntr::Config1, &RegSpace::SDRAM_control); |
Сообщ.
#9
,
|
|
|
вот хорошая статья на тему.
https://www.rsdn.org/article/cpp/fastdelegate.xml Цитата Теперь я, наверное, убедил вас в ужасности указателей на функции-члены. Но чем же они полезны? Пытаясь выяснить это, я провел большой поиск по коду, опубликованному в Интернете. И я нашел два общих случая использования указателей на функции-члены: надуманные примеры для демонстрации синтаксиса С++ новичкам; реализация делегатов! |
Сообщ.
#10
,
|
|
|
Статье 20 лет, она очень старая. И автор, похоже, просто не понимает толком, о чём пишет. Что вполне объяснимо, в то время язык большинством программистов ещё находился в стадии исследования.
По теории это всё. За практику ничего не буду.P.S. Большинство проблем с перекрёстным кастом указателей на методы снимается, если попросить компилятор всегда учитывать наихудшие случаи. Для MSVC это ключик /vmg. Тогда код в примере выше даже отработает правильно, ибо компилятор (наверное) нагенерит thunk к какому-нибудь аналогу "pure virtual call", и пусть они даже не вызываются, достаточно, чтобы в VMT что-то было и корректно позднесвязалось. Добавлено P.P.S. Статья реально очень старая. Уже 12 лет назад описанные в ней делегаты легко реализовывались биндами this в лямбды. В 2004-ом конечно всё было сложнее. И тем не менее даже тогда забиндить this в функциональный объект было несложно. Помню, как тут где-то в Холиварах писал подобное, только на предмет пропертей, а не делегатов. |
Сообщ.
#11
,
|
|
|
Qraizer, тема понятная давно, но с практической точки зрения, остается какой-то мутной, лично для меня. У тебя есть примеры, когда использование указателей на методы (а тем более - указатели на поля) класса - решали бы какие-то задачи, которые иначе решить невозможно. Ну или решение было бы такое оптимизированное, что стоило бы этим заморачиваться в обязательном порядке?
|
Сообщ.
#12
,
|
|
|
Ну а часто ли тебе нужен offsetof(), Majestio? Необходимость в указателях на поля будет не намного чаще. Но когда она возникнет, не будет стирания типов и уменьшится человеческий фактор из-за ошибок в адресной арифметике.
Я использовал. Не то чтобы прям без них никак, но удобно (кусочек): comPort[BitRate ]=uart_rate; comPort[StopBits]=stop_bits; comPort[fDTRCtrl]=DTR_CONTROL_ENABLE; // всегда готовы принимать comPort[fXonXoff]=FALSE; // управление передатчиком при приёме программных сигналов comPort[fNullRcv]= comPort[fErrRplc]= comPort[fErrCtrl]=FALSE; comPort[fBinary ]= // данные бинарные comPort[fParity ]=TRUE; comPort[readChar] =MAXDWORD; // при чтении ждём... comPort[readMult] =0; // ... с первого символа... comPort[readAdd ] =0; // ... и до первой паузы comPort[writeMult]=0; // записываем без пауз... comPort[writeAdd] =1000; // ... до секундого запрета передачи TProp operator[](IProp&) const; // Свойства TTimeOut operator[](ITimeOut&)const; class TProp { /* ... */ }; class IPropDWord: public IProp { /* ... */ }; class IPropWord: public IProp { /* ... */ }; class IPropByte: public IProp { /* ... */ }; class ITimeOut { /* ... */ }; IPropDWord BitRate (&DCB::BaudRate ); IPropByte ByteSize (&DCB::ByteSize ); IPropByte StopBits (&DCB::StopBits ); IPropByte Parity (&DCB::Parity ); /* ... */ ITimeOut readChar (&COMMTIMEOUTS::ReadIntervalTimeout); ITimeOut readMult (&COMMTIMEOUTS::ReadTotalTimeoutMultiplier); ITimeOut readAdd (&COMMTIMEOUTS::ReadTotalTimeoutConstant); ITimeOut writeMult(&COMMTIMEOUTS::WriteTotalTimeoutMultiplier); ITimeOut writeAdd (&COMMTIMEOUTS::WriteTotalTimeoutConstant); Вот ещё пример из кода заказчика. Шелезяка. У него восемь ADC, разбитных по 8-и регионам в адресном пространстве, и каждый сколько-то там регистров. В заголовках процессора ADC представлен структурой, а все ADC 8-ю соответствующими указателями на абсолютные адреса этих регионов. Удобно? Вполне. Но вот ОСи нередко требуется с ними обращаться в цикле. А циклу имена не передашь, он хочет индексы. Что они сделали? Правильно догадался: создали массив из указа... в смысле offsetof()-ов на поля, на Cях писано. Добавлено С указателями на методы аналогично. Но и сложнее. Вообще, было бы просто странно, если б были указатели на поля, но не было бы на методы. С другой стороны... вот когда нам нужна параметризация поведения, что мы делаем в C? У нас там есть указатели на функции. В Плюсах они, вообще говоря, уже не так востребованы, потому что у нас есть полиморфизм. Те же указатели, но скрытые под капотом и управляемые компилятором. Поэтому указатели на методы всплывают там, где требуется ещё более глубокая параметризация. Мне она понадобилась в том же контексте, что у заказчика с его ADC. Мне нужно было собрать методы в массив и индексировать. /* ... */ // внутренняя VMT - массив методов Accept(); // в отличие от RTTI-"матрицы" sheet, у каждого параметра мультиметода она своя и потому // должна строиться в run-time, чем конструкторы LastAcceptorCaller и занимаются ret_type (MD::*accepts[Length<typename CTL::Head::TypeList>::value])(param_type); /* ... */ // это все апцепторы в иерархии, кроме самого базового template <typename L, typename R, typename CTL, typename PTL, typename TPL, typename SRC, typename SHT, typename MD> struct IterAcceptorCaller<ret_type, TList<L, R>, CTL, PTL, TPL, SRC, SHT, MD> : public IterAcceptorCaller<ret_type, R, CTL, PTL, TPL, SRC, SHT, MD> { /*... */ /* восстановление типа очередного параметра, генерация следующего акцептора, передача ему следующего среза списка акцепторов, базовых типов параметров мультиметода, RTTI-матрицы итп и накопленной восстановленной информации о динамических типах и его вызов. */ typedef typename MostBase<typename CTL::Head::TypeList>::type most_base; typedef typename CTL::Head::template ParamType<most_base>::type param_type; ret_type Accept(param_type arg) { typedef typename SHT::base_type SheetType; typedef typename AcceptorMaker<ret_type, typename CTL::Tail, TList<arg_type, PTL>, typename TPL::base_type, SRC, SheetType>::type acceptor; // следующий акцептор acceptor a(base_type::m_Params, static_cast<typename TPL::base_type&>(base_type::m_Args), static_cast<const SheetType&>(base_type::m_Sht)); // поиск в RTTI-массиве элемента, соответствующего динамическому типу пришедшего в диспетчер // аргумента, и получение его индекса в исходном списке типов, описывающем его иерархию auto idx = std::lower_bound(std::begin(base_type::m_Sht.get()), std::end(base_type::m_Sht.get()), details::TypeInfo(CTL::Tail::Head::deref(GetField<0>(base_type::m_Args)))); // т.к. поиск не обязан давать точное совпадение и может только указать место в сортированном // контейнере, где ему было бы самое место, нужно самостоятельно убедиться в том, что элемент // успешно найден; в противном случае это означает ошибку в описании списка типов в иерархии if ( idx == std::end(base_type::m_Sht.get()) || !CTL::Tail::Head::check(*idx, CTL::Tail::Head::deref(GetField<0>(base_type::m_Args)))) throw std::invalid_argument("IterAcceptorCaller<> error: unlisted parameter"); // индексируем внутреннюю VMT, вызывая Accept() того акцептора, чей индекс вернул поиск // в RTTI-массиве, и передавая аргумент мультиметода, т.с. восстанавливая его тип; // в результате итерируемся к обработке следующего параметра return (a.self ->* a.accepts[idx->index()])(GetField<0>(base_type::m_Args)); } }; /* .. */ // это базовый в иерархии акцептор; весь список типов иерархии параметра уже обработан производными // классами, так что этот с ними уже не работает, он только обслуживает внутреннюю VMT template <typename CTL, typename PTL, typename TPL, typename SRC, typename SHT, typename MD> struct LastAcceptorCaller<ret_type, NullType, CTL, PTL, TPL, SRC, SHT, MD> /* ... */ Добавлено Вообще, наличие инструмента подразумевает, что где-то он полезен. Тот факт, что он маловостребован, не означает, что он не нужен. Если указатели на поля при их отсутствии можно моделировать offsetof(), то вот для указателей на методы альтернативы нет. К тому же, тут спорить не буду, легко можно не понимая инструмента, не заметить, что вот конкретно вот тут вот ему самое место и, даже если знаешь о его наличии, в итоге накодить костыль. |
Сообщ.
#13
,
|
|
|
Цитата Qraizer @ Ну а часто ли тебе нужен offsetof(), Majestio? Необходимость в указателях на поля будет не намного чаще. Но когда она возникнет, не будет стирания типов и уменьшится человеческий фактор из-за ошибок в адресной арифметике. Я использовал. Не то чтобы прям без них никак, но удобно (кусочек): Хе-хе Вставлю свои пять копеек. Ты же знаешь, ну или может быть помнишь - моей "нормальной" стартовой базой входа в программинг был Пасквиль, сперва Turbo, потом Virtual, и потом очень немножечко Дэлфи. Все эти "рéкорды" я полюбил сразу, как родных... Но потом я повстречал Perl Там только скаляры, вектора и хэши (на слэнге С++ "упорядоченные карты", ну или просто "карты") И сразу всё смотрится как-то иначе, а зачем нам куча предопределенных полей какой-то железяки, если в перспективе их будут тыщчы или даже стопицот, возможно тыщ??? Простой "Перловский" подход - "не знаешь что будет - загоняй всё в хэши" А-б-и-с-ь-н-я-ю свою точку зрения ... Линейка продуктов имеет в "прапертях" всё неугомонно расширяющуюся "палитру" прапертей, ну не угнацца щит (Q)! Но и мы сами с усами ... 1) Если не парить быстродействие, помним - загоняем всё в хэши 2) А если быстродействие парит - загоняем в вектора и именуем индексы enum'ами, при необходимости используем страшную клизму Про указатели на методы классов отпишу позже. Возможно и тут зарубимся по наносекундам, не хочется опережать события) Цитата Qraizer @ но удобно (кусочек) Если надо - код запилю. Но не мне тебе код писать, ты и сам уже всё прекрасно понимаешь и визуализируешь |
Сообщ.
#14
,
|
|
|
Цитата Majestio @ У "нас" загоняют в вектора. Ну, у меня был известен размер, так что массив. O(1) рулит. За наносекунды если... RTTI не рассматриваю, а вот вариант реализации на Visitor спорит с "нативной" реализацией, описанной у отца-основателя, но так и не реализованной в Стандарте. И это притом, что у него там двупараметрические, а у меня произвольные, причём ещё и с определяемой связностью параметров: где надо, позднее, где не надо, раннее. Как скажешь, так и будет. Ваши не конкуренты, в общем.Простой "Перловский" подход - "не знаешь что будет - загоняй всё в хэши" P.S. Интересно ещё было бы сравнить с языками с искаробочной поддержкой множественной динамической диспетчеризации. Насколько я понял, самая прозрачная поддержка у Common Lisp-а, но что-то я там не увидел декларативности в сопоставлении, только тупое полное перечисление всех вариантов комбинаций. Ну т.е. если у меня, скажем, три иерархии по десятку классов в каждой, то будь добр накидать 103 переопределений. Хотя могу ошибаться, не спец в нём. |
Сообщ.
#15
,
|
|
|
Скрытый текст Ну тя и чуйка! С 9 мая а ограничивал свою диету в плане алкоголя, и только сёння решился отведать студеной в морозильнике воткэ ... С разными очень вкусными яствами! Про отца-основателя не нужно - не асилим, давай на пальцах, ога? Я тебя спрашиваю про "практическое применение" а ты сорян "съезжаешь" в мульти-методы. Нет, мульти-методы "в вакууме" не нужны - нужен практический пример. Но и даже не тут !!! Мы же пока обсуждаем просто поля классов и ссылки на них. Про методы и мульти-методы поговорим потом. Итак, повтор вопроса, чем ценны ссылки на поля и пример их использования для достижения высшего качества? есть что сказать и показать на примерах? |