tags:

views:

883

answers:

1

I'm new to C++ CLI coming from unmanaged C++ world.

I'm getting this error:

candidate function(s) not accessible

when I pass a std::string as part of the method argument.

Here's the exact code:

Lib Project (compiled as .dll project)

//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 Project (compiled as 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;
}

Compiler Error:

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
+5  A: 

The problem is that std::string will compile as a internal (non public) type. This is actually a change in VS 2005+:

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

Native types are private by default outside the assembly Native types now will not be visible outside the assembly by default. For more information on type visibility outside the assembly, see Type Visibility. This change was primarily driven by the needs of developers using other, case-insensitive languages, when referencing metadata authored in Visual C++.

You can confirm this using Ildasm or reflector, you will see that your extract method is compiled as:

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

with basic_string being compiled as:

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

Note the internal.

Unfortunately you are then unable to call a such a method from a different assembly.

There is a workaround available in some cases: You can force the native type to be compiled as public using the make_public pragma.

e.g. if you have a method Extract2 such as:

void Extract2( std::exception& data_ );

you can force std::exception to be compiled as public by including this pragma statement beforehand:

#pragma make_public(std::exception)

this method is now callable across assemblies.

Unfortunately make_public does not work for templated types (std::string just being a typedef for basic_string<>) I don't think there is anything you can do to make it work. I recommend using the managed type System::String^ instead in all your public API. This also ensures that your library is easily callable from other CLR languages such as c#

Ben Schwehn