How do I marshal a unmanaged C++ map<> to be consumed by a .Net C# client?

HI:

I'm building a CLI/CPP layer to communicate between a native C++ library and a .Net (C#) client.

I have a map defined as the following in native C++
class FooClass;
std::map<int, FooClass> aMap;
SomeMethod(std::map<int, FooClass>);

Open in new window


I assumed that to call C# from my CLI/CPP layer, I would use a Dictionary<int, SomeFooClass>
When I attempt to do so, I get the following error:
Cannot marshal 'parameter #1': Generic types cannot be marshaled.

What is the best strategy for marshaling a map<> from native C++ through a CLI/CPP layer to
be consumed by a .Net Client?

Thanks,
JohnB
LVL 1
jxbmaSoftware ConsultantAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

jkrCommented:
Don't even try to, that won't work because of the nature of C++ templates. I'd rather try to keep all the lookup/insert functionality on the C++ side and provide functions or classes that encapsulate that functionality and are callable from .NET.
0
sarabandeCommented:
to add to above answer:

if the goal is to fill the std::map in native c++ and to solely use the map as a dictionary in c#, you can marshal a key-value pair of the std::map to an equivalent pair of a c# dictionary and then build the c# dictionary by retrieving all marshaled pairs from the std::map. if you do so, you actually have marshaled the std::map to a c# dictionary (which should be faster than to marshal each lookup operation) but won't go into reverse direction.

Cannot marshal 'parameter #1': Generic types cannot be marshaled.
marshaling in c# requires explicit types and doesn't allow templates to be used on neither side. that means marshal code at the c# doesn't permit variable things to happen (with the little exception of using StringBuilder).

a further reason is that in native c++ a class/struct defines a value-type which has an explicit storage for its members (similar to c types like int, char*, or arrays) while in c# all class-types were reference types (means they consist of a pointer to heap memory managed by gc). this difference makes it impossible to marshal generic types (template class types) since native c++ allows to use either class type or c type as template type with the same storage model while in c# the storage model necessarily must change from ref-type to value-type and vice-versa.
 
Sara
0
jxbmaSoftware ConsultantAuthor Commented:
jkr && Sara::>  I ended up breaking the dictionary into 2 arrays; 1 containing the keys and 1 containing the values. I'm not sure if this is the best model/pattern to follow, but it does work.

Some pseudo code would be as follows
map<integer, FooBarObject>
array<int>^ aKey;
array<FooBarObject^>^ aFooBars;

for (iterate over the key values )
{
    keyValue = From iterator;
    aKey[index] = keyValue;
    aFooBars[index] = MakeMeAManageFooBar(map[keyValue]);
}

Open in new window


For me it simplified the marshaling issue.
The dictionary will not contain thousands (or even hundreds of values).
On the .Net C# side, a templated Dictionary can always be created based on the arrays.

Thoughts?

Thanks,
JB
0
JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

jkrCommented:
I'd still keeep that thing un the unmanaged side, since that means only copying/marshalling teh elements that are really required. Even if it's only hundreds, transferring them all for a single lookup is quite an overhead if you can narrow it down to just pass a key to the managed encapsulation and get the result back. If there was an easy way to pass a reference of a std::map to .NET, that would be fine, but since there is no such thing, I'd go the way that puts as little performance penalty on the communication as possible. On the C# side, that would look like

namespace MyInterop
{
    interface IUnmanagedMap
    {
        object Lookup(System.string key);
        bool Insert(System.string key, object obj);
    }
}

Open in new window


If you require methods that allow you to iterate the collection, you could also add them.
0
sarabandeCommented:
if using an array at the c# side and fill it by using std::map::iterator you can do a binary search look-up since the elements were ordered by key.

int nbegin = 0;
int nend = num_items -1;
while (nend >= nbegin)
{
       int nmid = (nbegin+nend)/2;
       if ()
       {
            nbegin = nmid+1;
       }
       else if ( searchelement < myarray[nmid])
       {
            nend = nmid-1;
       }
       else
       {
             // element found
             break;
       }
}

Open in new window


Sara
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
jxbmaSoftware ConsultantAuthor Commented:
jkr::> Unfortunately, that approach won't work well with our problem set. The end client will essentially be using all the data in the returned data set. Originally, the unmanaged library was returning a vector, but the implementer decided to change it to a map<>  for easy of use on the client side. This works fine for the unmanaged client but adds pain to marshaling for the CLI/CPP bridge.

Sara::> Unfortunately, there might be holes in the indexes as entities enter/leave the snapshot window which gets returned with each callback, so the binary search won't work. But, as I said before the expected use model is to have 30 entities at the most (though typically 1-5 entries).

Thanks for the great feedback folks!

JB
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.