Привет. Хочу этого: есть приложение APP, есть модули в виде разделямых либ, которые дописываются в процессе, модули отправляют APP производные классы от базового интерфейсного, и APP с ними однотипно работает. Короче, обычный полиморфизм только между разными elf’ами, значит нельзя исключить личный комплект всяких libstdc++, libc, поэтому либо делать костыль в виде free() торчащий из модулей для удаления полученных APP’ом объектов, либо так (vector не будет перебрасываться между разными elf’ами, конечно же, просто для теста воткнул):
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Animal {
void (*speak)() = nullptr;
unique_ptr<Animal> (*copy)(Animal *p) = nullptr;
void (*print)(Animal *p) = nullptr;
void (*dstr)(Animal *p) = nullptr;
bool m_first_destruction = true;
~Animal() {
if (m_first_destruction) {
m_first_destruction = false;
(*dstr)(this);
}
}
};
struct Dog : Animal {
vector<int> m_v{34, 54, 543};
static unique_ptr<Animal> copy(Animal *p) {
Dog *ptr = static_cast<Dog*>(p);
return unique_ptr<Animal>{new Dog(*ptr)};
}
static void print(Animal *p) {
Dog *ptr = static_cast<Dog*>(p);
for (int v : ptr->m_v)
cout << v << endl;
}
static void dstr(Animal *p) {
Dog *ptr = static_cast<Dog*>(p);
ptr->~Dog();
}
Dog() : Animal{[](){cout << "woof\n";},
&Dog::copy,
&Dog::print,
&Dog::dstr} {}
};
struct Cat : Animal {
vector<int> m_v{2, 4, 1};
static unique_ptr<Animal> copy(Animal *p) {
Cat *ptr = static_cast<Cat*>(p);
return unique_ptr<Animal>{new Cat(*ptr)};
}
static void print(Animal *p) {
Cat *ptr = static_cast<Cat*>(p);
for (int v : ptr->m_v)
cout << v << endl;
}
static void dstr(Animal *p) {
Cat *ptr = static_cast<Cat*>(p);
ptr->~Cat();
}
Cat() : Animal{[](){cout << "meow\n";},
&Cat::copy,
&Cat::print,
&Cat::dstr} {}
};
int main() {
unique_ptr<Animal> cat{new Cat};
unique_ptr<Animal> cat_cp = cat->copy(cat.get());
cat_cp->speak();
cat_cp->print(cat.get());
unique_ptr<Animal> dog{new Dog};
unique_ptr<Animal> dog_cp = dog->copy(dog.get());
dog_cp->speak();
dog_cp->print(dog.get());
}
т.е. получаю от модуля unique_ptr, делаю его копию через copy(), и дергаю другие «виртуальные» методы. Все вроде норм, в целом устраивает, но есть две проблемки:
- Ругается гцц санитайзер на данный код (шланговский нет):
pavlick /tmp $ g++ 3.cc -fsanitize=address
pavlick /tmp $ ./a.out
meow
2
4
1
woof
34
54
543
=================================================================
==10127==ERROR: AddressSanitizer: new-delete-type-mismatch on 0x606000000200 in thread T0:
object passed to delete has wrong type:
size of the allocated type: 64 bytes;
size of the deallocated type: 40 bytes.
#0 0x7f4977684009 in operator delete(void*, unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cpp:172
#1 0x558db4b088ae in std::default_delete<Animal>::operator()(Animal*) const (/tmp/a.out+0x48ae)
#2 0x558db4b07c8a in std::unique_ptr<Animal, std::default_delete<Animal> >::~unique_ptr() (/tmp/a.out+0x3c8a)
#3 0x558db4b065c3 in main (/tmp/a.out+0x25c3)
#4 0x7f49770edb24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
#5 0x558db4b0620d in _start (/tmp/a.out+0x220d)
0x606000000200 is located 0 bytes inside of 64-byte region [0x606000000200,0x606000000240)
allocated by thread T0 here:
#0 0x7f4977682f41 in operator new(unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x558db4b068e9 in Dog::copy(Animal*) (/tmp/a.out+0x28e9)
#2 0x558db4b06543 in main (/tmp/a.out+0x2543)
#3 0x7f49770edb24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
SUMMARY: AddressSanitizer: new-delete-type-mismatch /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cpp:172 in operator delete(void*, unsigned long)
==10127==HINT: if you don't care about these errors you may set ASAN_OPTIONS=new_delete_type_mismatch=0
==10127==ABORTING
Я туплю или надо отправить багрепорт? Я так понимаю, что дело хитрого хака внутри деструктор Animal() он не понимает, видимо, что все деструкторы отрабатывают нормально.
- Ну и не очень нравится вот это:
unique_ptr<Animal> cat_cp = cat->copy(cat.get());
т.е. мне приходится дважды передавать указатель, что излишне, достаточно однажды это делать. Либо обмазывать макросами, либо закостылить что такое:
struct Base {
void (Base::*fp)() = nullptr;
};
struct Derived : Base {
void fd();
Derived(): Base{&fd} {}
};
чтобы потом дергать как-то так без дублирования (baseptr->*fp)(); Но я пока не могу сообразить как. Какие-нибудь идеи?