I want to use a C++ library that implements the Factory Method design pattern.
Below you can see a minimal reproducibable example, including C++ sources and the Ada adapter.
Item.h:
// Product interface
#ifndef _ITEM_H_
#define _ITEM_H_
class Item {
public:
virtual ~Item() = default;
virtual void do_something() = 0;
};
#endif
ConcreteItem.h:
#ifndef _CONCRETEITEM_H_
#define _CONCRETEITEM_H_
#include "Item.h"
class ConcreteItem : public Item {
public:
ConcreteItem();
~ConcreteItem();
void do_something();
};
#endif
ConcreteItem.cpp:
#include <stdlib.h>
#include <iostream>
#include "ConcreteItem.h"
void ConcreteItem::do_something() {
std::cout << "Doing stuff \n";
};
ConcreteItem::ConcreteItem() {
std::cout << "Concrete Item created \n";
};
ConcreteItem::~ConcreteItem(){
std::cout << "Concrete Item destroyed \n";
};
Factory.h:
#ifndef _FACTORY_H_
#define _FACTORY_H_
#include "Item.h"
class Factory {
public:
static Item* get_configured_item ();
Factory(Factory& other) = delete;
private:
static Factory* factory;
static Item* config_item;
Factory();
~Factory();
};
#endif
Factory.cpp:
#include "Factory.h"
#include "ConcreteItem.h"
Factory* Factory::factory = nullptr;
Item* Factory::config_item = nullptr;
Item *Factory::get_configured_item() {
if (Factory::factory == nullptr) {
Factory::factory = new Factory();
if (Factory::config_item == nullptr) {
Factory::config_item = new ConcreteItem();
};
};
return Factory::config_item;
};
Factory::Factory(){};
Factory::~Factory() {
delete Factory::config_item;
};
And here I have the Ada files that imports C++ symbols, generated with a call to g++ -c -fdump-ada-spec -C ./Factory.h
and doing some modifications to suite my taste:
limited with Item_h;
package Factory_h is
type Factory is limited record
null;
end record
with Import => True,
Convention => CPP;
function get_configured_item return access Item_h.Item'Class -- ./Factory.h:9
with Import => True,
Convention => CPP,
External_Name => "_ZN7Factory19get_configured_itemEv";
end Factory_h;
with Interfaces.C;
package Item_h is
type Item is limited interface
with Import => True,
Convention => CPP;
-- procedure Delete_Item (this : access Item) is abstract;-- ./Item.h:8
--
-- procedure Delete_And_Free_Item (this : access Item) is abstract; -- ./Item.h:8;
procedure do_something (this : access Item) is abstract; -- ./Item.h:9
end Item_h;
And finaly an Ada main to test this silly example:
with Factory_h;
with Item_h;
procedure main is
configured_item : constant access Item_h.Item'Class :=
Factory_h.get_configured_item;
begin
configured_item.do_something;
end main;
Do you know why if I comment out the Item primitives Delete_Item
and Delete_And_Free_Item
the call to do_something
is never done and the item is destroyed?
If I uncomment them everything works.
Thanks in advance!
I'm far from expert on Ada-C++ interfacing, but my guess is that in the C++ code, do_something is the third entry in the virtual-function table for ConcreteItem. The dump-ada-spec operation adds Delete_Item and Delete_And_Free_Item to occupy the first two slots in the v-table on the Ada side, which makes do_something the third entry in the v-table, matching its position on the C++ side.
When you comment out Delete_Item and Delete_And_Free_Item this makes do_something be the first entry in the v-table on the Ada side. So when Ada calls do_something, it actually calls the first v-table entry, which on the C++ side is some other function -- apparently one that performs Delete_Item.