Лучший обходной путь для ошибки компилятора C2158: make_public не поддерживает собственные типы шаблонов - PullRequest
5 голосов
/ 08 ноября 2010

У меня есть два c ++ / cli dll (т.е. скомпилированные с / clr), где A.dll ссылается на B.dll. В сборке B у меня есть метод GetMgdClassB, который я хотел бы вызвать из сборки A. Вот код в сборке B (B.cpp):

namespace B
{
    public class NativeClassB
    {
    public:
        NativeClassB();
        // ... 
    };

    public ref class MgdClassB
    {
    public:
        static MgdClassB ^ GetMgdClassB(const std::vector<NativeClassB *> & vecNativeBs)
        {
            // ...
            vecNativeBs;
            return gcnew MgdClassB();
        }
    };
}

Обратите внимание, что метод GetMgdClassB принимает std :: vector. В сборке A я пытаюсь вызвать этот метод с помощью следующего кода (A.cpp):

namespace B
{
    class NativeClassB;
}

#pragma make_public(std::vector<B::NativeClassB *>)

namespace A
{
    void Foo()
    {
        std::vector<B::NativeClassB *> vecNativeBs;
        B::MgdClassB::GetMgdClassB(vecNativeBs);
    }
}

Когда я компилирую A.cpp, я получаю следующую ошибку:

error C2158: 'std::vector<_Ty>' : #pragma make_public directive is currently supported for native non-template types only

причина, по которой я хотел добавить эту прагму, заключается в том, что по умолчанию собственные типы являются частными для сборки. Если я удаляю прагму, я получаю следующую ошибку (как и ожидалось):

error C3767: 'B::MgdClassB::GetMgdClassB': candidate function(s) not accessible

, поскольку тип экземпляра шаблона std::vector<B::NativeClassB *> является закрытым для сборки.

Попытки решения

1. Используйте void *, безопасность типа прерывания:

Измените метод, GetMgdClassB, чтобы взять void * и передать адрес std::vector<NativeClassB *> методу. В GetMgdClassB. Затем я могу static_cast передать в void * до std::vector<NativeClassB *> *. Это, конечно, работает, но нарушает безопасность типов.

2. Создайте управляемую оболочку для NativeClassB, передайте управляемый контейнер

Создайте управляемый класс, скажем ref class NativeClassBWrapper, единственной целью которого является сохранение ссылки на нативный NativeClassB. Измените GetMgdClassB на управляемый контейнер NativeClassBWrappers (например, List<NativeClassBWrapper ^> ^). Недостатком этого является необходимость создания и заполнения нового управляемого контейнера перед вызовом GetMgdClassB, а затем в управляемом классе B я должен упаковать его в собственный контейнер std::vector<NativeClassB *> (поскольку код в B имеет дело с этим типом.

В настоящее время я склоняюсь к решению № 1, поскольку (а) оно не вызывает проблем с производительностью и (б) я буду делать это только в нескольких случаях. Мне не нравится терять безопасность типов, но это кажется оправданным, учитывая текущий недостаток способности компилятора делать видимыми типы создания экземпляров шаблонов.

Вопрос:

Есть ли лучшие способы обхода?

Связанный вопрос:

C ++ CLI error C3767: функции-кандидаты недоступны

Ответы [ 2 ]

2 голосов
/ 10 ноября 2010

Я получил решение от Майка Дейнса на другом форуме:

http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/b43cca63-b0bf-451e-b8fe-74e9c618b8c4/

По сути, решение состоит в том, чтобы создать собственную сборку (назовите ее VectorOfNativeB) в сборке B, которая содержит указатель или ссылку на std :: vector. Экспортируйте VectorOfNativeB и сделайте его публично видимым. Измените метод GetMgdClassB, чтобы он брал указатель или ссылку VectorOfNativeB.

[разместил это здесь для дальнейшего использования и чтобы узнать, есть ли у кого-нибудь здесь какие-либо комментарии об этом решении].

2 голосов
/ 08 ноября 2010

Я не знаю ни одного способа экспорта этого типа. Если вам нужна подпись этой функции, я бы предпочел использовать сочетание управляемого и собственного экспорта (управляемые функции, использующие собственные типы, в любом случае не могут использоваться другими языками), и, возможно, использовать задержку загрузки при вызове собственного экспорта, так что у вас есть шанс перехватить ошибки при поиске сборки обычным способом .NET.

Но ваша конкретная функция может быть проблематичной, поскольку она использует как управляемые типы, так и сложные собственные типы в сигнатуре.

В целом, лучшая практика заключается в том, чтобы вообще не передавать собственные классы C ++ через границы DLL, так как это настраивает вас на нарушения одного правила определения.

Для этой конкретной ситуации я предлагаю создать оболочку, которая реализует ICollection. Это решает проблему так же, как ваше решение №2, без необходимости фактически копировать все элементы в новую структуру данных.

...