5

I am exporting a method that can be called from unmanaged code, but the function itself lives in a managed c++ project. The following code results in a compiler error:

error C2526: 'System::Collections::Generic::IEnumerator<T>::Current::get' : C linkage function cannot return C++ class 'System::Collections::Generic::KeyValuePair<TKey,TValue>'
error C2526: 'System::Collections::Generic::Dictionary<TKey,TValue>::KeyCollection::GetEnumerator' : C linkage function cannot return C++ class 'System::Collections::Generic::Dictionary<TKey,TValue>::KeyCollection::Enumerator'

extern "C"
__declspec( dllexport )
bool MyMethod(std::string &name, std::string &path, std::map<std::string, std::string> &mymap)
{
  System::Collections::Generic::Dictionary<System::String ^, System::String^> _mgdMap = gcnew System::Collections::Generic::Dictionary<System::String ^, System::String ^>();

  // Blah blah processing
}

Looking into this error a little bit, the issue normally has to do with the definition of the method that is marked as 'extern "C"'. So why would it be at all concerned with what goes on inside the method?

If I comment out the Dictionary initialization, or switch it to a HashTable everything compiles beautifully.

The following works as well - if instead of defining a dictionary locally, I avoid the local variable by initializing it in a method.

bool status = CallAnotherMethod(ConvertToDictionary(mymap));

where ConvertToDictionary is declared as

System::Collections::Generic::Dictionary<System::String ^, System::String ^>^ ConvertToDictionary(std::map<std::string, std::string> &map)
{
}

Which tells me that this is a seemingly arbitrary error. I would still like to understand why the compiler thinks this is a problem.

2
  • Were you able to managed this? Seems that i have similar issue - not able to init C# class from C++/CLI, i believe that this is because "extern C" requires compiler to know the size of your dictionary or class before it can be created, it is rule of "C" language - interface goes before implementation and it tells compiler how much memory this object needs to be initialized, Hans Passant answered it here stackoverflow.com/questions/2718836/… Commented Oct 12, 2014 at 15:22
  • the thing is that my class was defined in C# and i do not want to duplicate its implementation in C++/CLI - how do i inform C++/CLI what size is required by this class? Commented Oct 12, 2014 at 15:24

3 Answers 3

2

Sorry for necroposting but i need to share my happiness with somebody :)

Seems that you are able to solve this just by creating two wrappers - one for external call marked with "extern C" and one for internal call. In this case everything outside "extern C" will be executed as usual .NET code.

That was answered by the topicstarter here - C++/CLI->C# error C2526: C linkage function cannot return C++ class

void DummyInternalCall(std::string &name, std::string &path, std::map<std::string, std::string> &mymap)
{
   System::Collections::Generic::Dictionary<System::String ^, System::String^> _mgdMap =  gcnew System::Collections::Generic::Dictionary<System::String ^, System::String ^>();

  // Blah blah processing
}

extern "C" __declspec( dllexport )
bool MyMethod(std::string &name, std::string &path, std::map<std::string, std::string> &mymap)
{
   DummyInternalCall(name, path, mymap);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, that's what I said in my question too. It didn't seem very logical, but it did work. Looking at the question that you linked, I guess the explanation there makes sense.
2

If you write a function in C++ that is to be called from C, you can't use anything in it's interface (arguments and return type) that isn't plain C. Here all your arguments are C++ objects.

2 Comments

none of the std:: parameters are resulting in the compilation error. That is not the problem.
@Liz: Probably your compiler errors out at some other place then. You can not use C++ constructs in an extern "C" linkage function prototype. For example all those references: How are you going to pass those from a compilation unit written in C? C doesn't know references.
1

We not need write wrapper function, We only need in C++ linkcage scope force instantiate template.



template<typename T>
struct Foo {
    T* ptr;
};


void force_instantiate_template() {
    Foo<int> x; // remove this line 
    // MSVC will compile error 
    // https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2526?view=msvc-170
    // clang warning. 
    // gcc no warning.

}

struct Bar { int x ; };
extern "C" {
    Bar bar2() {
      return {};
    }

    Foo<int> foo() {
        return { .ptr = nullptr } ;
    }

    void bar(Foo<int>) {

    }
}

godbolt

2 Comments

... are you trying to do explicit expansion instantiation?
Yes, you can see more details in llvm issues.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.