У меня есть очень большая и зрелая база кода C++, для которой я пытаюсь использовать SWIG для создания интерфейса C#. Я не могу изменить сам код C++, но мы можем использовать все, что предлагает SWIG, для его расширения/обновления. Я столкнулся с проблемой, когда функция C++, написанная, как показано ниже, вызывает проблемы в C#.
A* SomeClass::next(A*)
Вызывающий может сделать что-то вроде:
A* acurr = 0;
while( (acurr = sc->next(acurr)) != 0 ){
if( acurr isoftype B ){
B* b = (B*)a;
...do some stuff with b..
}
elseif( acurr isoftype C )
...
}
По сути, итерация по контейнеру элементов, который, в зависимости от их истинного типа, делает что-то другое. Сгенерированный SWIG слой С# для "следующей" функции, к сожалению, делает следующее:
return new A();
Таким образом, вызывающий код в C# не может определить, является ли возвращаемый объект фактически производным классом или нет, на самом деле он всегда кажется базовым классом (что имеет смысл). Я наткнулся на несколько решений:
- Используйте ключевое слово %extend SWIG, чтобы добавить метод к объекту и в конечном итоге вызвать dynamic_cast. Недостатком этого подхода, на мой взгляд, является то, что он требует от вас знания иерархии наследования. В моем случае это довольно огромно, и я вижу, что это проблема обслуживания.
- Используйте ключевое слово %factory для указания метода и производных типов, и SWIG автоматически сгенерирует код dynamic_cast. Это кажется лучшим решением, чем первое, однако при более глубоком рассмотрении оно по-прежнему требует от вас поиска всех методов и всех возможных производных типов, которые он может вернуть. Опять же, огромные проблемы с обслуживанием. Я хотел бы иметь ссылку на документ для этого, но я не могу ее найти. Я узнал об этой функциональности, просматривая пример кода, поставляемый с SWIG.
- Создайте метод C# для создания экземпляра производного объекта и передачи cPtr в новый экземпляр. Хотя я считаю это неуклюжим, это действительно работает. См. пример ниже.
public static object castTo(object fromObj, Type toType) { object retval = null; BaseClass fromObj2 = fromObj as BaseClass; HandleRef hr = BaseClass.getCPtr(fromObj2); IntPtr cPtr = hr.Handle; object toObj = Activator.CreateInstance(toType, cPtr, false); // make sure it actually is what we think it is if (fromObj.GetType().IsInstanceOfType(toObj)) { return toObj; } return retval; }
Это действительно варианты? И если я не хочу копаться во всех существующих функциях и производных классах, то мне остается № 3? Любая помощь будет оценена по достоинству.