Forums

Overridden ++ and extension method woes

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Overridden ++ and extension method woes

Postby CodexArcanum » Mon May 03, 2010 1:03 am

Hi, me again. I'm apparently on a quest to drive Chuck insane, since I think I've stumbled onto another potential issue. I can start adding things to the bug tracker if that would be better, or I can keep suggesting things here for confirmation.

The basic situation is that a class in libtcodwrapper, Background, is overriding `++` for some nefarious purposes. It's bad design, I think, but that's a discussion for some other time. Here's the overridden operator in C#
Code: Select all
public class Background
{
        internal int m_value;

        /// <summary>
        /// Increment background type to next background in BackgroundFlag enum
        /// </summary>
        /// <param name="lhs">Left Hand Side</param>
        /// <returns>New Background</returns>
        public static Background operator ++(Background lhs)
        {
            if (lhs.BackgroundFlag == BackgroundFlag.Alph)
                throw new Exception("Can not increment past end of BackgroundFlag enum");
            lhs.m_value += 1;
            return lhs;
        }
}


Cobra won't recognize the ++ operator, even overridden, and I had no luck trying to fake it with stuff like ".++" and ".plusplus". I decided to take a crack at extending Background with a new method ".increaseFlag" that looks like:
Code: Select all
extend Background
        def increaseFlag
            .m_value += 1


But I get an error message suggesting that "m_value" cannot be found in "this" of type "Background". So I'm guessing maybe extension methods can't see the internal scoped members.


I also don't know that this is a bug or a bug worth fixing right now, since I don't know the long-term strategy for Cobra to incorporate the various backends. Most of my complaints have been C# specific and I'm feeling a bit guilty in that regard. Anyway, I'll be glad to provide more details or implement any workarounds that are suggested. Thanks!
CodexArcanum
 
Posts: 21

Re: Overridden ++ and extension method woes

Postby Charles » Mon May 03, 2010 5:30 am

Yeah this is actually a poor practice on the part of the library because operator overloads are not part of the .NET Common Type System (CTS) which defines the baseline of capabilities that .NET languages are required to support in order to share libraries. What happens is that some library creators forget about (or perhaps don't care about) .NET's multi-language nature and stray away from CTS.

Also, you're correct that extension methods cannot read "internal" members. However, "reflection" might let you bypass this at run-time. Something like the following:
use System.Reflection

extend Background

def increaseFlag
op = .getType.getMethod('op_Increment', BindingFlags(DeclaredOnly, Static, Public))
op.invoke(this, nil)

I haven't tested this. If you have problems getting it working, let me know.

You can use a similar trick if you need access to the members, such as:
fi = .getType.getField('m_foo', BindingFlags(Instance, Public, NonPublic, GetField))

You can look some of these up by searching MSDN for "getField" and "BindingFlags". Or if I happen to be a Google search box, I just slap an "msdn" in there.

We already have the requisite tickets:
-- http://cobra-language.com/trac/cobra/ticket/70
-- http://cobra-language.com/trac/cobra/ticket/69

Please keep posting to the discussion forums. Different users have different development styles and use different libraries. The only way for me to find out about this stuff is via the discussion forum. In fact, I consider such feedback a form of contribution, so no need to feel guilty.

If something needs a ticket after being discussed, I'll ask for it.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Overridden ++ and extension method woes

Postby Charles » Mon May 03, 2010 6:15 am

Woops. How could I forget to mention the "sharp string" workaround?

extend Background

def increaseFlag
sharp'_lh_this++'

You could also use sharp'b++' wherever you plan to use this and skip the extension method. Although I like to keep my sharp string use minimal, so if I was using this in more than one place, I would go with the extension method.

The general form is:
sharp'blah blah'
# or
sharp"blah blah"

The contents are passed verbatim to the C# compiler. This can be used not only for statements, but anywhere an expression is expected. It cannot be used for declarations.

The Cobra compiler assigns a special type called passthrough to sharp strings. Often this is sufficient, but when it's not, you'll need to typecast with "to int" or what-have-you.

For types, methods, arguments and local vars that you create yourself, their names translate to C# literally. The only exception is for identifiers that clash with C#'s keywords. These will be prefixed with @ as in "@foo" which is the syntax C# provides for this purpose.

But for special things like the hidden "this" argument in an extension method, you have to do a little detective work. For example, "cobra -keep-intermediate-files ..." and then look around foo.cobra.cs to see what Cobra generated. When I use sharp strings outside of extension methods, I rarely, if ever, tap into any specially generated variables. I'm typically just using my own locals and arguments.

Sharp strings provide a nice escape hatch when you need it. As Cobra has become more mature, we've needed them less and less, to the point where I had almost forgotten about them!

HTH
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Overridden ++ and extension method woes

Postby CodexArcanum » Mon May 03, 2010 11:09 am

Nice! Thanks for the workarounds, those are both excellent ideas. I'll have to keep the sharp-string in mind as well if ever I need to access some specific language feature (though so far I've been pretty satisfied with what Cobra translates).

A note on how the implementation went for me:

I tried the sharp-string first, but got an error:
Main.cobra(29): error: "libtcodWrapper.Background" does not contain a definition for "m_value"
from the code
Code: Select all
extend Background
        def increaseFlag(bg as Background?)
            sharp"bg.m_value++"


So it looks like the internal method is still out of reach to me through that methodology. So I opted for reflection instead, which worked very nicely:

Code: Select all
extend Background
        def increaseFlag(bg as Background?)
            op = .getType.getMethod('op_Increment', BindingFlags(DeclaredOnly, Static, Public))
            op.invoke(this, @[bg])


I'd also like to point out how much nicer that was to implement in Cobra than in C# (I use reflection occasionally at work). I'm not sure if the BindingFlags constructor is built-in (if so I really need to use it more often) or a Cobra thing, but it's a lot better than "BindingFlags.Foo | BindingFlags.Bar" And the invoke is much cleaner using the array literal than the equivalent C#, which I think would be "new object[] { bg }".
CodexArcanum
 
Posts: 21

Re: Overridden ++ and extension method woes

Postby Charles » Mon May 03, 2010 12:01 pm

I think the sharp string would have worked as:
sharp"bg++"

Since the ++ operator overload is on the Background class.

Also, your extension methods don't need an argument for Background since it is available as "this" (or "_lh_this" in the generated C#). Here a String extension for example:
extend String

def doubleLength as int
return .length * 2

class Program

def main
s = 'aoeu'
assert s.doubleLength == 8

It's as if you were writing a method in the original class declaration with the exception that you don't get access to non-public members.

Yes, for enums that combine you can write: EnumName(Foo, Bar, Baz)

And arrays can be done as @[1, 2, 3] where the type of the array is inferred as the most specific type that encompasses all members.

Glad you're getting along.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Overridden ++ and extension method woes

Postby CodexArcanum » Mon May 03, 2010 12:16 pm

D'oh. That string does work, my bad. Ok, so that's a lot better yeah.
CodexArcanum
 
Posts: 21


Return to Discussion

Who is online

Users browsing this forum: No registered users and 42 guests