6

I see a lot of entries for doing this in C#, but nothing for C++

I have a set of properties in some managed C++ code that is used to pass the data between the C# portion and c++ portion. In the C# side, the answer presented here works very well and I'd like to do something similar with C++. Copy of solution contained in Link:

string NameOf<T>(Expression<Func<T>> expr) {
    return ((MemberExpression) expr.Body).Member.Name;
}

var gmtList = new SelectList(repository.GetSystemTimeZones(),
    NameOf(() => tz.Id),
    NameOf(() => tz.DisplayName));

My problem is that I can't seem to get the correct syntax for the call, specifically, this section:

() => tz.DisplayName

I can't seem to find a resource online that goes over how I would do this in C++, so if anybody has any experience or links, I would really appreciate any help.

Community
  • 1
  • 1
Fry
  • 4,106
  • 9
  • 38
  • 51

3 Answers3

6

I've struggled with all those lambdas and cool System::Linq namespaces (including the CLinq library and other things) for a couple of hours and then... I just thought: why not use a purely static decision ?

We just declare the

/// Convert X to "X", the classical preprocessing trick
#define GetPropName(TheClassName, ThePropertyName) #ThePropertyName

and then we can do

Console::WriteLine( GetPropName(TimeZone, Id) );

to get the "Id" printed on the screen.

Yeah... Now the interesting part. The type safety. I hear the comment storm coming about this solution ("NO ! This is not good, it does not check if the ThePropertyName is in the class !")

OK. The solution: let us produce some meaningless code using a macro which uses the ThePropertyName in a dummy TheClassName's instance.

/// This macro will produce the compilation error if ThePropertyName is not in the class named TheClassName
#define CheckForPropertyExistence(TheClassName, ThePropertyName) \
/* Create an array of Objects which will be converted to string and ignored*/ \
(gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString()

/// We get the property name using the "dinosaur strategy" - good old macro concatenated with the empty string which in turn is formed in CheckFor() macro
#define GetPropertyName(TheClassName, ThePropertyName) \
(gcnew System::String(#ThePropertyName)) + CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0)

Now we can give the complete sample:

using namespace System;

/// Sample class
public ref class TheTimeZone
{
public:
    TheTimeZone()
    {
        _Id = 0;
        _DisplayName = "tmp";
    }

    property int Id
    {
    public:
        int get() {return _Id;}
        void set(int v) { _Id = v; }
    }

    property String^ DisplayName
    {
    public:
        String^ get() { return _DisplayName; }
        void set(String^ v) { _DisplayName = v; }
    }

private:
    int _Id;
    String^ _DisplayName;
};

/// This macro will produce the error if ThePropertyName is not in the class named TheClassName
#define CheckForPropertyExistence(TheClassName, ThePropertyName) \
/* Create an array of Objects which will be converted to string and ignored*/ \
(gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString()

/// We get the property name using the "dinosaur strategy":
/// good old macro concatenated with the empty string
/// which in turn is formed in CheckFor() macro
#define GetPropertyName(TheClassName, ThePropertyName) \
(gcnew System::String(#ThePropertyName)) + \
CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0)

/// To get properties from objects with no default constructor
#define GetPropertyNameForObject(TheObject, ThePropertyName) \
(gcnew System::String(#ThePropertyName)) + \
(gcnew array<System::Object^> { (TheObject)-> ThePropertyName })->ToString()->Substring(0,0)

/// Test for our macros
int main(array<System::String ^> ^args)
{
    /// Prints "Length"
    /// We cannot use default constructor here
    Console::WriteLine(GetPropertyNameForObject (gcnew System::String("test"), Length) );

    /// Prints "Id"
    Console::WriteLine(GetPropertyName (TheTimeZone, Id) );

/// Uncomment and get the error
    //Console::WriteLine(GetPropertyName (TheTimeZone, Id23) );

    return 0;
}
Viktor Latypov
  • 14,289
  • 3
  • 40
  • 55
  • 2
    Quite beautiful/hacky... Couldn't you have decorated the CheckForPropertyExistence with a `1 ? NULL : yourcode`, so that at runtime there were two less objects created? – xanatos May 11 '15 at 11:02
  • @xanatos: Yes, it seems to be a good idea. Three years have passed, I don't remember the exact reasoning to create an object. Maybe just the "solution found !" excitement prevented me from doing as you propose. – Viktor Latypov May 11 '15 at 14:05
2

Lambda expressions in C# are syntactic sugar for delegates, so you will have to find the delegate equivalent of the lambda expression to be able to have the same functionality in C++/CLI.

To make your life a bit easier, you could explore CLinq (see the "Lambda expressions" section) that provides a C++/CLI wrapper for Linq

Note: Do not confuse the C++11 lambda expressions with the C# lambda expressions. The former is supported only for native code. It is possible to use the C++11 lambdas, but you need to do some extra work to provide delegates for them (this CodeProject article explores the subject)

Attila
  • 28,265
  • 3
  • 46
  • 55
  • There is no "delegate equivalent" which is compatible with the C++/CLI and managed runtime. However, there are overlooked macros ! – Viktor Latypov May 22 '12 at 09:00
  • @ViktorLatypov - Please read about C++/CLI delegates [here](http://www.drdobbs.com/cpp/184401980). – Attila May 22 '12 at 10:28
  • Sorry, Attila. I meant Lambdas with the " X => F(X) " syntax which are used in the original question. System.Delegate is perfectly valid, but there is no easy way to decompile the code it actually refers to. – Viktor Latypov May 22 '12 at 10:29
  • Indeed, hence the suggestion to use `CLinq` that tries to allow something very similar to the C# lambdas – Attila May 22 '12 at 10:40
  • I've spent an hour inside the CLinq (sure, it is good) and it was because of the intent to mimic the C# lambda used in the original solution. But then I just realized there's a one-line macro-based solution which relies not on the .net feature which I use quite often. – Viktor Latypov May 22 '12 at 10:46
2

I realize this is an old thread, but something like this might also work:

String^ NameOfSanityCheck(Object^ object, String^ name) { return name; } // could parse with a regex string like: "(->)|(::)|(\.)"

#define nameof(s) NameOfSanityCheck(s, #s)
Burl Rice
  • 21
  • 1