Articles about OrangeC to .NET
Table of Contents
Introduction
To allow the support of .NET methods, classes, properties, enums .... has been implemented in OrangeC, a support for C# like language, for example:
int a = 10;
printf("%s", a.ToString());
Another feature, that came from C++, has been enabled to use is the "using namespace
", for example:
using namespace System;
using namespace System::IO;
So, let's look how to use those new features of OrangeC compiler to .NET.
Using .NET Types
The .NET types are nothing more than .NET classes, so, with OrangeC, you can use these classes and methods as normal types in C. To make it more simple to understand, let's build a small example, using a .NET string
class:
int main()
{
System::String str("Hello World!!");
return 0;
}
With this code, we created an instance of class String
, for convenience, was implementing inside of compiler, some keywords to handle the most common classes of .NET, that is:
Object
String
So, to use these types using the native keyword, we have:
__object
__string
Let's look at a small example:
int main()
{
__string str("Hello World!!");
__object obj = (__object)str;
printf("%s\n", str);
printf("%s\n", (__string)obj);
return 0;
}
Let's look at one more of using .NET types example:
int main()
{
System::DateTime dt(2017,10,10);
return 0;
}
To avoid problems of compatibility between legacy code and new codes, a syntactic variation was implemented to differentiate between arrays types, so, to create a managed array, we use the []
before the variable name, and, to create an unmanaged array, we use the []
after the variable name. To make it more clear, let's make a small example:
int main()
{
int []a = { 0, 1, 2, 3, 4, 5 }; int b[] = { 0, 1, 2, 3, 4, 5 };
return 0;
}
Maybe you are thinking, what exactly is the difference between the managed and unmanaged array?
So, to answer this, let's look at the generated code to each kind of array (I have used a decompiler to get this code):
public static unsafe int main(int argc, void* argv)
{
int[] a = new int[] { 0, 1, 2, 3, 4, 5 };
int32[24] b = null;
*(&b + 4) = 1;
*(&b + 8) = 2;
*(&b + 12) = 3;
*(&b + 16) = 4;
*(&b + 20) = 5;
return 0;
}
The navigation into array is still the same:
int main()
{
int []a = { 0, 1, 2, 3, 4, 5 }; int b[] = { 0, 1, 2, 3, 4, 5 };
printf("Printing all elements of managed array: ");
for(int i=0;i<a.Length;i++) {
if(i != 0)
printf(", ");
printf("%d", a[i]);
}
printf("\n");
printf("Printing all elements of unmanaged array: ");
for(int i=0;i<sizeof(b)/sizeof(int);i++)
{
if(i != 0)
printf(", ");
printf("%d", a[i]);
}
printf("\n");
return 0;
}
Using .NET Methods
The usage of .NET methods is like C#:
<Instance>.<Method>([args]);
The difference is with enum
s and static
class
[Namespace]::<Static class>::<Method | Property | Enum>[ ( [args] ) ];
Let's look at one single example, that shows these cases:
#include <stdio.h>
#include <stdlib.h>
using namespace System;
using namespace System::Runtime;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
int main()
{
AppDomain ad = AppDomain::CurrentDomain;
AssemblyName am("TestAsm");
AssemblyBuilder ab = ad.DefineDynamicAssembly(am, AssemblyBuilderAccess::Save);
ModuleBuilder mb = ab.DefineDynamicModule("testmod", "TestAsm.exe");
TypeBuilder tb = mb.DefineType("mytype", TypeAttributes::Public);
MethodBuilder metb = tb.DefineMethod("hi", MethodAttributes::Public |
MethodAttributes::Static, NULL, NULL);
ab.SetEntryPoint(metb);
ILGenerator il = metb.GetILGenerator();
il.EmitWriteLine("Hello World");
il.Emit(OpCodes::Ret);
tb.CreateType();
ab.Save("TestAsm.exe");
Console::WriteLine("TestAsm.exe created!");
return 0;
}
Let's build one more example, implementing a function that receives a .NET type as parameter and uses some sophisticated methods call:
#include <stdio.h>
void test(System::DateTime dt, System::String &str)
{
str = dt.ToString("dd/MM/yyyy");
}
int main()
{
__string str;
System::DateTime dt1 = System::DateTime::Today;
System::DateTime dt2 = dt1.AddDays(30);
str = dt1.ToString("dd,MM,yyyy") + "/" + dt2.ToString("dd.MM.yy");
__string part1 = str.Substring(0, str.IndexOf("/"));
__string part2 = str.Substring(str.IndexOf("/") + 1);
str = str.Replace(",", "").Replace("/", "").Replace("10", "Hi!");
printf("Part1 = %s\nPart2 = %s\nNew string = %s\n", part1, part2, str);
test(dt1, str);
printf("%s\n", str);
return 0;
}
Finally, we will build a mix of managed and unmanaged code, for this, we will build a linked list, where the struct
of linked list hold managed types:
#include <stdio.h>
#include <stdlib.h>
int getAge(System::DateTime reference, System::DateTime birthday);
typedef struct node
{
__string name;
System::DateTime bornDate;
struct node* next;
} node_t;
void push(node_t* head, __string name, System::DateTime born)
{
node_t* current = head;
while (current->next != NULL)
current = current->next;
current->next = (node_t*)malloc(sizeof(node_t));
current->next->name = name;
current->next->bornDate = born;
current->next->next = NULL;
}
void print_list(node_t * head) {
node_t * current = head;
System::DateTime today = System::DateTime::Today;
while (current != NULL) {
System::Console::WriteLine(((__string)"").PadLeft(80, System::Convert::ToChar("-")));
System::Console::WriteLine("Name: {0}\nBorn date: {1}\nAge: {2}\n",
current->name, current->bornDate.ToString("dd/MM/yyyy"), getAge(today, current->bornDate));
current = current->next;
}
}
int getAge(System::DateTime reference, System::DateTime birthday)
{
int age = reference.Year - birthday.Year;
if(System::DateTime::op_LessThan(reference, birthday.AddYears(age)))
age--;
return age;
}
int main()
{
node_t* head = (node_t*)malloc(sizeof(node_t));
head->name = "Alexandre";
head->bornDate = System::DateTime::Today;
head->next = NULL;
System::DateTime today = System::DateTime::Today;
for(int i=0;i<15;i++)
push(head, "Teste", today.AddDays(i + 1).AddYears(-24));
print_list(head);
return 0;
}
Using Try / Catch / Finally
Exception systems greatly help the developer to handle errors so that C code becomes more compatible with the .NET error system and exception system, we implement support for try
/ catch
/ finally
, the usage of these commands like in C#, so, let's take a look:
using namespace System;
using namespace System::IO;
int main()
{
__try
{
File.Open("some file that doesn't exist", FileMode::Open);
}
__catch (Exception e)
{
Console::WriteLine(e.Message);
}
__finally
{
printf("Finally!");
}
}
History
- 12-Oct-2017: Article created