0

Im a c# dev trying to get into c++, and Im writing some custom controls. I need the c++ equivalent of the following complex c# Dictionary

private static Dictionary<PinchscapeColor, 
        Dictionary<PinchscapeColorLevel, Brush>> AccentColorMap;

PinchscapeColor and PinchscapeColorLevel are simple c# enums

public enum PinchscapeColorLevel
{
    Light,
    Medium
    Dark
}

public enum PinchscapeColor
{
    PinchscapeCyan,
    PinchscapeLime,
    PinchscapeMagenta,
    PinchscapeTangerine,
    PinchscapePlum
}

and I calculate a particular color/color level combination like this (in c#)

var color = AccentColorMap[PinchscapeColor.PinchscapeCyan][PinchscapeColorLevel.Dark];

my attempts to do this in c++ have succeeded up to a point:

my enums:

public enum class PinchscapeColorLevel
{
    Light,
    Medium,
    Dark
};

public enum class PinchscapeColor
{
    PinchscapeCyan,
    PinchscapeLime,
    PinchscapeMagenta,
    PinchscapeTangerine,
    PinchscapePlum
};

ive defined a map in my header file like this

Platform::Collections::Map<PinchscapeBasicControls::PinchscapeColor,
    Platform::Collections::Map<PinchscapeBasicControls::PinchscapeColorLevel,
    Windows::UI::Xaml::Media::Brush^>^>^ colorMap;

but Im getting the following compiler error:

program files (x86)\microsoft visual studio 11.0\vc\include\collection.h(1118): error C3986: 'Invoke': signature of public member contains native type 'std::less<_Ty>' (that's the first line, it goes on forever)

Does anyone have any ideas what Im doing wrong ? Id have imagined that this was gonna be easy :(

EDIT

Please find below a minimal code example that is all the code you need to replicate the issue:

1) Class1.h

#pragma once

namespace WindowsRuntimeComponent1
{
    public enum class ColorLevelEnum
    {
        Light,
        Medium,
        Dark
    };

    public enum class ColorEnum
    {
        Cyan,
        Lime,
        Magenta,
        Tangerine,
        Plum
    };

    public ref class Class1 sealed
    {
    private:
        Windows::Foundation::Collections::IMap<WindowsRuntimeComponent1::
            ColorEnum,Windows::Foundation::Collections::IMap<WindowsRuntimeComponent1::
            ColorLevelEnum,Windows::UI::Xaml::Media::Brush^>^>^ colorMap;
    public:
        Class1();
    };
}

Class1.cpp

#include "pch.h"
#include <collection.h>
#include "Class1.h"

using namespace WindowsRuntimeComponent1;
using namespace Windows::UI::Xaml::Media;
using namespace Platform::Collections;
using namespace Platform;

Class1::Class1()
{
    if (colorMap == nullptr)
    {
        colorMap = ref new Map<ColorEnum,Map<ColorLevelEnum,Brush^>^>();
    }
}

hope that helps you recreate the issue

Thanks to anyone who is taking the time to help me sort this out

Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
  • One problem could be that `public enum class PinchscapeColor` is not valid C++ syntax. Are these enums declared inside a class? – juanchopanza Aug 25 '12 at 10:29
  • they are declared in a header file, and they are the same as Microsoft sample code for c++/cx – Dean Chalk Aug 25 '12 at 10:43
  • C++ enums don't have access modifiers, and `enum class` is the C++11 way of declaring a type-safe enum. I'm not familiar with C++/cx though so this might be an extension. (Btw, isn't there a cx tag yet? If not you should probably make one). – Cubic Aug 25 '12 at 10:59
  • _"it goes on forever"_ The first line of the diagnostic indicates where the error was detected. If there are multiple lines (as there often are), subsequent lines often tell you what was being compiled when this occurs (e.g., the stack of templates that were being instantiated when the error occurred) or the set of options from which the compiler was unable to make a selection (e.g., there were 10 overloads, none of which matched). In short, the rest of the lines are often far more interesting than the first line, especially when the first line indicates that Standard Library code is at fault – James McNellis Aug 25 '12 at 18:33

1 Answers1

1

Your code as it is presented in the question is well-formed and compiles without error using the RTM release of Visual Studio 2012.

I can speculate, though, as to the cause of your problem: it is likely that you are using this colorMap (or its type) as a public or protected member of a public ref class or that you are using it in some way that its type ends up being the type of a public or protected member.

The Platform::Collections::Map type is a C++/CX-specific implementation of the interface Windows::Foundation::Collections::IMap. When declaring public members of a Windows Runtime type, you need to use IMap, not Map, since Map relies on C++- and C++/CX-specific language features that are not usable across the Windows Runtime ABI.

In short, if you change your colorMap to use IMap, it should solve the problem:

IMap<
    PinchscapeColor,
    IMap<
        PinchscapeColorLevel,
        Brush^
    >^
>^ colorMap;

When you instantiate this, you'll need to instantiate the outer map first, then each of the inner maps, using he Platform::Collections::Map. For example:

// Create the outer map; note the "value type" is still IMap<T, U>
colorMap = ref new Map<ColorEnum,IMap<ColorLevelEnum,Brush^>^>();

// Insert an element into the outer map:
colorMap->Insert(ColorEnum::Cyan, ref new Map<ColorLevelEnum, Brush^>());

Consider using some typedefs to make the code clearer:

typedef IMap<PinchscapeColorLevel, Brush^>          IColorLevelBrushMap;
typedef IMap<PinchscapeColor, IColorLevelBrushMap^> IColorMap;

IColorMap^ colorMap;

Alternatively, if you don't need to make this map accessible to other Windows Runtime components, consider using std::map, which will perform measurably better:

typedef std::map<PinchscapeColorLevel, Brush^>        ColorLevelBrushMap;
typedef std::map<PinchscapeColor, ColorLevelBrushMap> ColorMap;

ColorMap colorMap;

Or:

typedef std::pair<PinchscapeColor, PinchscapeColorLevel> ColorMapKey;
typedef std::map<ColorMapKey, Brush^>                    ColorMap;

ColorMap colorMap;
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Yes but you cant instantiate an IMap, and if you change the instantiation to a MAP instead, the error comes back. – Dean Chalk Aug 26 '12 at 17:42
  • I get a feeling that we are all barking up the wrong tree, and the issue is using an enum as a dictionary key. After a TON of googling I get an inkling that you need to use a type comparer in the Map constructor, but I can find any examples - Im sure this must be easy - I cant believe there isn't a simple solution. – Dean Chalk Aug 26 '12 at 17:45
  • If you can post a minimal, complete, self-contained example that demonstrates the error and what you are trying to accomplish, I can help you figure out what the solution is. You can assuredly use an enumeration as a key type. You cannot instantiate an `IMap`, no, but `Map` implements the `IMap` interface. You may not need a WinRT map at all, though: `std::map` may be sufficient for most uses. – James McNellis Aug 26 '12 at 19:16
  • minimal code example added to the question - thanks in advance for helping me solve this frustrating issue :) – Dean Chalk Aug 27 '12 at 08:40
  • Also an std::map would suffice, as Im not exposing this type over the ABI - I just started with a Map because Im reading up on C++/CX – Dean Chalk Aug 27 '12 at 08:41
  • I've updated the answer. `Map>` is convertible to `IMap>`, but `Map>` is not (it would be convertible to `IMap>`). Hope that helps. If not, let me know. I would definitely use `std::map` though if you don't need to access this map from another component. – James McNellis Aug 27 '12 at 15:34
  • My newer code does use IMap as you suggest, and it doesn't compile, Ive tried using std::map and that doesn't compile either. I have the latest Visual Studio as available today from MSDN subscriber downloads – Dean Chalk Aug 28 '12 at 05:40
  • This is my test stub that demonstrates correct, working code as described in the answer: https://gist.github.com/80ac68a45565db3b0710. Is your code materially different? – James McNellis Aug 28 '12 at 05:49
  • Im very sorry for wasting your time James, I hadn't properly declared the inner Map as an IMap, and now I have done that it works :) I cant get std::map to work as I dont know the interface I need to use for the inner std::map (there is no std::imap, and i get the same compile error when using std::map). Also the tyedef recommendations cause a load of new compiler errors, so I'm going to give those a miss until I have a better understanding of C++. Thenk you SOOOO much for your help I REALLY appreciate it :) – Dean Chalk Aug 28 '12 at 08:00
  • @DeanChalk: No problem at all. If you're looking for a good introductory C++ book, I'd recommend _C++ Primer, 5th Edition_. There's a link to it on [The Definitive Book Guide and List](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). It's just been released and covers modern C++ style, and is very well written. I'd guess that it can be rough learning both C++ and C++/CX at the same time; it might be best to learn C++ first, then learn how C++/CX extends C++ for targeting WinRT. I could be wrong, of course; _everyone_ is new to this, including me. :-) – James McNellis Aug 28 '12 at 15:28