С++ CLI ошибка C3767: функция кандидата недоступна

Я новичок в С++ CLI из неуправляемого мира С++.

Я получаю эту ошибку:

candidate function(s) not accessible 

когда я передаю std::string как часть аргумента метода.

Здесь точный код:

Lib Project (скомпилирован как проект .dll)

//Lib.h

#pragma once

public ref class Lib
{
public:
  Lib(void);

public:
  void Extract( std::string& data_ );
};

//Lib.cpp

#include "Lib.h"

Lib::Lib(void)
{
}

void Lib::Extract( std::string& data_ )
{
  data_.empty();
}

Проект LibTest (скомпилирован как application.exe)

//LibTest.h

#pragma once

ref class LibTest
{
public:
  LibTest(void);
};

//LibTest.cpp

#include "LibTest.h"

LibTest::LibTest(void)
{
  Lib^ lib = gcnew Lib;
  lib->Extract( std::string("test") );
}

int main()
{
  return 0;
}

Ошибка компилятора:

1>------ Build started: Project: LibTest, Configuration: Debug Win32 ------
1>Compiling...
1>LibTest.cpp
1>.\LibTest.cpp(7) : error C3767: 'Lib::Extract': candidate function(s) not accessible

Ответы

Ответ 1

Проблема заключается в том, что std::string будет компилироваться как внутренний (не публичный) тип. Это фактически изменение VS 2005 +:

http://msdn.microsoft.com/en-us/library/ms177253(VS.80).aspx:

Собственные типы являются закрытыми по умолчанию вне сборки Теперь родные типы не будут отображаться вне сборки по умолчанию. Дополнительную информацию о видимости типа вне сборки см. В разделе Тип видимости. Это изменение в основном было обусловлено потребностями разработчиков, использующих другие, не зависящие от регистра языки языки, при ссылке на метаданные, созданные в Visual С++.

Вы можете подтвердить это с помощью Ildasm или reflector, вы увидите, что ваш метод extract скомпилирован как:

public unsafe void Extract(basic_string<char,std::char_traits<char>,std::allocator<char> >* modopt(IsImplicitlyDereferenced) data_)

с базовым_строком, скомпилированным как:

[StructLayout(LayoutKind.Sequential, Size=0x20), NativeCppClass, MiscellaneousBits(0x40), DebugInfoInPDB, UnsafeValueType]
internal struct basic_string<char,std::char_traits<char>,std::allocator<char> >

Обратите внимание на внутреннее.

К сожалению, вы не можете вызвать такой метод из другой сборки.

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

например. если у вас есть метод Extract2, например:

void Extract2( std::exception& data_ );

вы можете принудительно скомпилировать std:: exception как общедоступный, предварительно включив этот pragma-оператор:

#pragma make_public(std::exception)

этот метод теперь можно вызывать через сборки.

К сожалению, make_public не работает для шаблонных типов (std::string просто для typedef для basic_string < > ) Я не думаю, что вы можете сделать что-нибудь, чтобы заставить его работать. Я рекомендую использовать управляемый тип System:: String ^ во всех ваших общедоступных API. Это также гарантирует, что ваша библиотека легко может быть вызвана с других языков CLR, таких как С#

Ответ 2

если вы просто должны получить доступ к методам internal, другая работа будет делать проекты как Friend Assemblies следующим образом:

//Проект Lib

#pragma once

//define LibTest as friend assembly which will allow access to internal members
using namespace System;
using namespace System::Runtime::CompilerServices;
[assembly:InternalsVisibleTo("LibTest")];

public ref class Lib
{
 public:
  Lib(void);

 public:
  void Extract( std::string& data_ );
};

//Проект LibTest

#pragma once

#using <Lib.dll> as_friend

ref class LibTest
{
  public:
    LibTest(void);
};

Ответ 3

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