Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ CLI error C3767: candidate function(s) not accessible

Tags:

c++-cli

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
like image 259
sivabudh Avatar asked Jun 03 '09 20:06

sivabudh


3 Answers

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#

like image 134
Ben Schwehn Avatar answered Nov 13 '22 09:11

Ben Schwehn


if you simply must access the internal methods another work around would be making the projects as Friend Assemblies like that:

//Lib Project

#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 Project

#pragma once

#using <Lib.dll> as_friend

ref class LibTest
{
  public:
    LibTest(void);
};
like image 45
makc Avatar answered Nov 13 '22 09:11

makc


In addition to the solutions described above, one can subclass the templated type to obtain a non-templated type, and include its definition in both projects, thus overcoming some of the problems mentioned above.

like image 2
Mihai Avatar answered Nov 13 '22 10:11

Mihai