1

In C it is possible to write a macro function that replaces an input with the input as a string.

#define x(k) {#k, k}

'(4)' would generate '{"4", 4}'

I have a usecase in C# where i want to pass an input like this to a unit test.

private void AssertInt64Expression(Int64 i, string str)
{
    Assert.AreEqual(i, MathFactory.make(str));
}

[Test]
public void ParseBasic()
{
    AssertInt64Expression(4, "4");
    AssertInt64Expression(2+3, "2+3");
    AssertInt64Expression(7-11, "7-11");
    AssertInt64Expression(7-11 *2, "7-11 *2");
    AssertInt64Expression(7  -  11 *  2, "7  -  11 *  2");
}

I am essentially repeating the information (including whitespace) here, how can i use something like the c style macro to solve this in c#?


edit:

essentially i would love to write:

private void AssertInt64Expression(GeneratorMagic magic)
{
    Assert.AreEqual(magic.ToCode(), MathFactory.make(magic.ToString()));
}

[Test]
public void ParseBasic()
{
    AssertInt64Expression(<#7  -  11 *  2#>);
}

I am aware that this would not compile.


edit:

I added a code snippet as an answer to illustrate what i am looking for. However this snippet runs very slow, since i need it to refactor my unit tests into cleaner code with less repetition i need the snippet to run faster. The snippet essentially provides the magic from the previous edit as a KeyValuePair.

Johannes
  • 6,490
  • 10
  • 59
  • 108
  • 1
    Not sure what actually you are trying to do but [Code Generation and T4 Text Templates](http://msdn.microsoft.com/en-us/library/bb126445.aspx) may help. – Mehmet Ataş Sep 04 '14 at 14:00
  • 1
    According to your [comment there](http://stackoverflow.com/questions/709463/c-sharp-macro-definitions-in-preprocessor#comment40110288_709475), you found the question [C# Macro definitions in Preprocessor](http://stackoverflow.com/questions/709463/c-sharp-macro-definitions-in-preprocessor). Is your question not answered there (the answer being "No")? – CodeCaster Sep 04 '14 at 14:01
  • @CodeCaster I found the answer and i am not looking for a preprocessor. I have a usecase where i want to write somethign once rather than twice and i am looking how this is done in C#. It seems unlikely that this is impossible. – Johannes Sep 04 '14 at 14:08
  • 2
    *"It seems unlikely that this is impossible."* I disagree, this is very likely impossible. – abelenky Sep 04 '14 at 14:13
  • It seems like you could do this with expressions, something like `AssertThing(() => 7 - 11 * 2)`; where AssertThing takes an `Expression>`, and walk the expression tree to turn it into a string? – Michael Edenfield Sep 04 '14 at 14:23
  • possible duplicate of [c# evaluating string "3\*(4+2)" yield int 18](http://stackoverflow.com/questions/333737/c-sharp-evaluating-string-342-yield-int-18) – Justin Sep 04 '14 at 14:28
  • Nevermind; constant folding apparently happens before the expression gets created. – Michael Edenfield Sep 04 '14 at 14:28

2 Answers2

0

You can use a custom number class with overloaded operators.

static void Main(string[] args)
{
    Console.WriteLine((Number)1 + 5);
    Console.WriteLine((int)((Number)1 + 5 + 6));

}

public class Number
{
    private string _representation = "";
    private int _number = 0;

    private Number(int n)
    {
        _number = n;
        _representation = n.ToString();
    }

    public Number Plus(int n)
    {
        _representation += " + " + n;
        _number += n;
        return this;
    }

    public static Number operator +(Number value1, int value2)
    {
        return value1.Plus(value2);
    }

    public static explicit operator Number(int val)
    {
        return new Number(val);
    }

    public static explicit operator int(Number num)
    {
        return num._number;
    }

    public override string ToString()
    {
        return _representation;
    }
}
brz
  • 5,926
  • 1
  • 18
  • 18
  • the point would be to feed the string input into my math generator and compare its result with an expected result that is essentially what C# would calculate without the generator. Your example would work for a limited set and fail if i wanted to test against the influence of whitespace. "1 + 1" is different from "1+1". – Johannes Sep 04 '14 at 14:22
  • You can add a method to Number class to generate all possible white space configurations for given input. – brz Sep 04 '14 at 14:26
0

The following snippet does what i need but it seems to run horribly slow.

    private KeyValuePair<String, Int64> GenerateCodeInt64(String mathKey)
    {
        string codeNamespace = "MathTestCalculator";
        string codeClassName = "MathTestCalculator";
        string codeMethodName = "Value";

        Int64 I64Value = 0;
        StringBuilder codeBuilder = new StringBuilder();

        codeBuilder
            .Append("using System;\n")
            .Append("namespace ").Append(codeNamespace).Append(" {\n")
            .Append("class ").Append(codeClassName).Append("{\n")
            .Append("public Int64 ").Append(codeMethodName).Append("(){\n")
            .Append("return (Int64)(").Append(mathKey).Append(");}}}\n");

        CompilerParameters cp = new CompilerParameters();
        cp.GenerateExecutable = false;
        cp.GenerateInMemory = true;

        CompilerResults results = CodeDomProvider
            .CreateProvider("CSharp")
            .CompileAssemblyFromSource(cp, codeBuilder.ToString());

        if (results.Errors.Count > 0)
        {
            StringBuilder error = new StringBuilder();
            error.Append("Unable to evaluate: '").Append(mathKey).Append("'\n");
            foreach (CompilerError CompErr in results.Errors)
            {
                error
                    .Append("Line number ").Append(CompErr.Line)
                    .Append(", Error Number: ").Append(CompErr.ErrorNumber)
                    .Append(", '").Append(CompErr.ErrorText).Append(";\n");
            }
            throw new Exception(error.ToString());
        }
        else
        {
            Type calcType = results.CompiledAssembly.GetType(codeNamespace + "." + codeClassName);
            object o = Activator.CreateInstance(calcType);
            I64Value = (Int64)calcType.GetMethod(codeMethodName).Invoke(o, null);
        }
        return new KeyValuePair<string, long>(mathKey, I64Value);
    }
Johannes
  • 6,490
  • 10
  • 59
  • 108