E>Понятно, что можно завести реестр объектов и проверять указатель на принадлежность, но хотелось бы обойтись без этого. E>По идее можно проверить vptr, но к нему кроскомпиляторно не подберешься, да и всякие случаи вроде множественного наследования...
Абстрактный виртуальный kind_of в базовом классе.
Типа:
enum ST {
eS2, eS3
};
struct S {
virtual ST kind_of() const = 0;
};
struct S2 : S {
virtual ST kind_of() const { return eS2; }
};
struct S3 : S {
virtual ST kind_of() const { return eS3; }
};
int main(int argc, char* argv[])
{
S2 s;
S3 s2;
void *p = &s;
void *p2 = &s2;
ST a = ((S*)p)->kind_of();
ST b = ((S*)p2)->kind_of();
return 0;
}
Но ИМХО стремно это как то, если потерять контроль над кодом.
Здравствуйте, nen777w, Вы писали:
N>Абстрактный виртуальный kind_of в базовом классе.
Это все конечно здорово, вот только не всегда работает
ST kindOf(void* p) { return ((S*)p)->kind_of(); }
int i;
kindOf(&i); // упсstruct One { int t; };
struct Two : One, S2 {};
Two t;
kindOf(&t); // если Two хранится в памяти как (One, S2) - будут грабли
Здравствуйте, TarasB, Вы писали:
TB>Здравствуйте, enji, Вы писали:
E>>Хочется узнать, указывает ли void* на конкретный тип TB>Крестопроблемы отсутствия общего предка в иерархии, да?
конечно если у тебя бесконечная память, ты можешь позволить себе пихать указатель на vptr+RTTI в каждый объект
только как производительностью запахнет — так сразу С да С++
Здравствуйте, Abyx, Вы писали:
A>конечно если у тебя бесконечная память, ты можешь позволить себе пихать указатель на vptr+RTTI в каждый объект A>только как производительностью запахнет — так сразу С да С++
Здравствуйте, TarasB, Вы писали:
A>>конечно если у тебя бесконечная память, ты можешь позволить себе пихать указатель на vptr+RTTI в каждый объект A>>только как производительностью запахнет — так сразу С да С++
TB>Ок, а почему нельзя вызвать typeid?
typeid вернет "void*" потому что оно не знает где брать RTTI для объекта.
Здравствуйте, enji, Вы писали:
E>Понятно, что можно завести реестр объектов и проверять указатель на принадлежность, но хотелось бы обойтись без этого.
Где и почему происходит переход к void*? Возможно этот переход можно заменить на что-то типа boost::any?
E>По идее можно проверить vptr, но к нему кроскомпиляторно не подберешься, да и всякие случаи вроде множественного наследования...
Да и не поможет он в общем случае. void* может указывать на double, который случайно может выглядеть как правильный vptr.
Здравствуйте, Abyx, Вы писали: A>typeid вернет "void*" потому что оно не знает где брать RTTI для объекта.
Почему нельзя все объекты, для которых ты хочешь знать тип, унаследовать от MyBaseRootObject, и вместо void* брать MyBaseRootObject*?
И typeid надо брать не от указателя, а от того, что по нему.
Здравствуйте, TarasB, Вы писали:
TB>Здравствуйте, Abyx, Вы писали: A>>typeid вернет "void*" потому что оно не знает где брать RTTI для объекта.
TB>Почему нельзя все объекты, для которых ты хочешь знать тип, унаследовать от MyBaseRootObject, и вместо void* брать MyBaseRootObject*? TB>И typeid надо брать не от указателя, а от того, что по нему.
Здравствуйте, Abyx, Вы писали:
TB>>Почему нельзя все объекты, для которых ты хочешь знать тип, унаследовать от MyBaseRootObject, и вместо void* брать MyBaseRootObject*? TB>>И typeid надо брать не от указателя, а от того, что по нему.
A>ты вообще знаешь как работает RTTI?
яркое доказательство того, кому на самом деле не нравится С++ =)
зы
TarasB — один из самых ярых ненавистников С++ на говноког.ру
(сужу не только по нику)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, enji, Вы писали:
E>>Понятно, что можно завести реестр объектов и проверять указатель на принадлежность, но хотелось бы обойтись без этого.
EP>Где и почему происходит переход к void*? Возможно этот переход можно заменить на что-то типа boost::any?
boost::any не умеет доставать указатели по иерархии, только в точности то, что туда положили. Есть попытка dynamic_any (гугл в помощь), но там какие-то принципиальные сложности были, ЕМНИП, я помню было обсуждение в буст-листе, по наводке alnsn
Еще можно boost::variant, если известен набор возможных базовых классов.
Но если этот void* — это просто замыкание в стиле Си (коллбэк + контекст) или какой-нть GetWindowLong (если я правильно помню заклинание), то, имхо, ничего тут проверять не надо, достаточно просто в документации написать, что по этому указателю обязан быть наследник Base.
Либо действительно завести реестр, но компилировать его, естественно, только в отладочной версии.
Здравствуйте, jazzer, Вы писали:
E>>>Понятно, что можно завести реестр объектов и проверять указатель на принадлежность, но хотелось бы обойтись без этого. EP>>Где и почему происходит переход к void*? Возможно этот переход можно заменить на что-то типа boost::any? J>boost::any не умеет доставать указатели по иерархии, только в точности то, что туда положили. Есть попытка dynamic_any (гугл в помощь), но там какие-то принципиальные сложности были, ЕМНИП, я помню было обсуждение в буст-листе, по наводке alnsn
Да, any_cast требует точного типа. Я имел ввиду своё type-erasure (возможно на Boost.TypeErasure), которое даст возможность неинтрузивно сделать все необходимые проверки, выполнить все операции, но:
J>Еще можно boost::variant, если известен набор возможных базовых классов.
этот вариант лучше/проще.
Кстати, наткнулся на вот это обсуждение (ты про него говорил? там и alnsn отметился).
Там is_derived проверка делается через abuse исключений.
J>Но если этот void* — это просто замыкание в стиле Си (коллбэк + контекст) или какой-нть GetWindowLong (если я правильно помню заклинание), то, имхо, ничего тут проверять не надо, достаточно просто в документации написать, что по этому указателю обязан быть наследник Base.
Никогда не делал проверки в подобных местах — не было таких требований надёжности.
Здравствуйте, TarasB, Вы писали:
TB>Здравствуйте, enji, Вы писали:
E>>Хочется узнать, указывает ли void* на конкретный тип TB>Крестопроблемы отсутствия общего предка в иерархии, да?
да как скзать. С одной стороны — да, с другой — тут погоня за миниоптимизациями, которые в "некрестах" просто невозможны Можно было сделать это все в плюсовом стиле на boost::signals2 к примеру, таких проблем бы не возникло
Здравствуйте, jazzer, Вы писали:
J>Почему у тебя void*, а не Base*, для начала?
Есть собственный мини-фреймворк для сигналов-слотов, с разными плюшками, вроде предобработки сигнала, фильтрации и т.д. Пока сделал его так, что наследование от Base (да и к тому ж виртуального Base) не требуется ни для источника, ни для получателей. И по пути boost::function (с наворачиванием шаблонного наследника интерфейса) идти пока не охота — обошелся просто указателями на объект и на статическую функцию.
Дальше в рамках решения прикладной задачи захотелось написать универсальный фильтр, которому как раз хорошо бы понять тип получателя. Пока обошелся просто внешним реестром. Но задумался, нельзя ли понять тип динамически, исследую структуру объекта, на который указывает void*
J>Но если этот void* — это просто замыкание в стиле Си (коллбэк + контекст) или какой-нть GetWindowLong (если я правильно помню заклинание), то, имхо, ничего тут проверять не надо, достаточно просто в документации написать, что по этому указателю обязан быть наследник Base.
Вот кстати тож интересный вопрос
struct Base {
virtual ~Base();
virtual int f();
};
struct Some { int a; };
struct S1 : Some, Base {} s1;
struct S2 : Base {} s2;
struct S3 : Base, Some {} s3;
int func(void *o) {
return static_cast<Base*>(o)->f();
}
func(&s1); // упс
func(&s2); // ок
func(&s3); // скорее всего ок
Как я понимаю, на msvc и gcc надо потребовать, чтобы Base был "первым" наследником
Здравствуйте, enji, Вы писали:
E>Здравствуйте, jazzer, Вы писали:
J>>Почему у тебя void*, а не Base*, для начала?
E>Есть собственный мини-фреймворк для сигналов-слотов, с разными плюшками, вроде предобработки сигнала, фильтрации и т.д. Пока сделал его так, что наследование от Base (да и к тому ж виртуального Base) не требуется ни для источника, ни для получателей. И по пути boost::function (с наворачиванием шаблонного наследника интерфейса) идти пока не охота — обошелся просто указателями на объект и на статическую функцию.
А почему бы в таком случае не использовать boost::function напрямую (или вообще boost::signal)? Он шаблонов не требует, у него тип нормальный, а в замыкание при конструировании что угодно можно засунуть, не нужно никаких дополнительных void*-параметров.
E>Дальше в рамках решения прикладной задачи захотелось написать универсальный фильтр, которому как раз хорошо бы понять тип получателя. Пока обошелся просто внешним реестром. Но задумался, нельзя ли понять тип динамически, исследую структуру объекта, на который указывает void*
Нет, нельзя. dynamic_cast мог бы, но он не работает с void*, ему нужен указатель на объект реального класса.
Здравствуйте, jazzer, Вы писали:
J>А почему бы в таком случае не использовать boost::function напрямую (или вообще boost::signal)? Он шаблонов не требует, у него тип нормальный, а в замыкание при конструировании что угодно можно засунуть, не нужно никаких дополнительных void*-параметров.
велосипедостроение потому что boost::signal напрямую использовать не получится — хочется фильтрацию и приоритеты вызовов. function кушает 16 байт и дает вызов по указателю на член + вызов виртуальной функции. Мои два указателя кушают 8 байт и дают простой вызов функции по указателю. Хотя конечно function значительно более удобен для оборачивания всяких лямбд. Может быть и перееду в конце концов на него.
Однако к первоначальному вопросу это отношения не имеет — хотелось определять тип источника сигнала и предпринимать некоторые действия в зависимости от этого.
Судя по этой теме, никакой волшебной пули нет — или надо в качестве источника иметь некий объект с доп информацией, или внешний реестр источников.
Здравствуйте, enji, Вы писали:
E>Здравствуйте, jazzer, Вы писали:
J>>А почему бы в таком случае не использовать boost::function напрямую (или вообще boost::signal)? Он шаблонов не требует, у него тип нормальный, а в замыкание при конструировании что угодно можно засунуть, не нужно никаких дополнительных void*-параметров.
E>велосипедостроение потому что boost::signal напрямую использовать не получится — хочется фильтрацию и приоритеты вызовов. function кушает 16 байт и дает вызов по указателю на член + вызов виртуальной функции. Мои два указателя кушают 8 байт и дают простой вызов функции по указателю. Хотя конечно function значительно более удобен для оборачивания всяких лямбд. Может быть и перееду в конце концов на него.
Если ты знаешь, какие типы могут быть, и при этом не хочешь наследовать все от одного базового типа, но знаешь все возможные базы — попробуй boost::variant с этим набором возможных базовых типов.
Здравствуйте, TarasB, Вы писали:
TB>Здравствуйте, jazzer, Вы писали:
J>>Нет, нельзя. dynamic_cast мог бы, но он не работает с void*, ему нужен указатель на объект реального класса.
TB>Ну так подсунь ему не void* а BasicRootClassOfAllClasses, все нужные классы наследуй от него.
у него там int в примере есть, как нафиг BasicRootClassOfAllClasses
Здравствуйте, TarasB, Вы писали:
TB>Здравствуйте, jazzer, Вы писали:
J>>у него там int в примере есть, как нафиг BasicRootClassOfAllClasses
TB>Пусть обернёт в класс тогда этот свой инт.
Здравствуйте, enji, Вы писали:
E>Хочется узнать, указывает ли void* на конкретный тип E>По идее можно проверить vptr, но к нему кроскомпиляторно не подберешься, да и всякие случаи вроде множественного наследования...
В общем случае, это невозможно, void* — это слишком хорошо стёртый тип. Особенно, с учётом того, что можно взять указатель на неполиморфную базу полиморфного наследника — она ничем не отличается от члена-данного, кроме тайного знания, что это не член, а база.
Если класс полиморфный, то обычная практика — поместить vfptr в самое начало лэяута. (Это не оговаривается стандартом, так же, как не оговаривается вообще механизм виртуальных функций и rtti; но MS- и linux-совместимые компиляторы делают именно так).
При этом неполиморфные базы смещаются, а если есть несколько полиморфных баз — то первая из них будет с нулевым смещением, а остальные со своими vfptr-ами — как придётся.
(Тут я хотел написать много слов про dynamic_cast, в том числе про dynamic_cast<void*>, но проще будет отослать к первоисточникам и толкованиям — стандарту и Страуструпу).
Разумеется, dynamic_cast применительно к неполиморфным объектам — это неопределённое поведение. Потому что, представьте себе, что там по нулевому смещению находится указатель на какую-нибудь мусорную таблицу виртуальных функций. Можно получить всё, что угодно, от защиты памяти до исполнения произвольного кода.
Отсюда мораль
— или сдерживать себя и ограничиться какой-нибудь строгой иерархией (не void*, а MyRootType*)
— или сделать внешнюю информацию о типе — хоть boost::any, хоть tagVARIANT рядом держать, — способов много
Здравствуйте, enji, Вы писали:
E>велосипедостроение потому что boost::signal напрямую использовать не получится — хочется фильтрацию и приоритеты вызовов. function кушает 16 байт и дает вызов по указателю на член + вызов виртуальной функции. Мои два указателя кушают 8 байт и дают простой вызов функции по указателю. Хотя конечно function значительно более удобен для оборачивания всяких лямбд. Может быть и перееду в конце концов на него.
Если источники сигнала всегда приходят типизированными, и только внутри твоей библиотеки заворачиваются в void*, то можно всегда брать их внутрь шаблонным методом/конструктором и класть рядом с void* указатель на шаблонную реализацию интерфейса, через котрый можно всё разрулить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском