Forward C++ Exceptions from CLI to C# - c#

I am wrapping a C++ library via CLI into C#.
File from C++ Library:
/* cpp_lib/Date.hpp */
namespace cpp_lib
{
struct Date
{
Date(int year, int month, int day)
{
if (1 > month || month > 12)
throw std::out_of_range("Month is out of range");
...
}
};
}
File from CLI Wrapper:
/* cli_lib/Date.hpp */
#include <cpp_lib/Date.hpp>
#include "ManagedObject.hpp"
namespace CLI_Lib
{
public ref class Date : public ManagedObject<cpp_lib::Date>
{
public:
Date(int year, int month, int day);
: ManagedObject(new cpp_lib::Date(year, month, day)) { }
};
}
where ManagedObject.hpp comes from Creating a C++/CLI Wrapper.
Sample C# Script:
/* Program.cs */
using CLI_Lib;
namespace My_Script
{
internal class Program
{
static void Main(string[] args)
{
try
{
Date d1 = new Date(2025, 12, 25);
Date d2 = new Date(2026, 13, 25); // Will throw because 13 is not a month
Console.WriteLine("Dates are OK");
}
catch (Exception e)
{
Console.WriteLine($"ERROR: {e.ToString()}");
}
}
}
}
The C# Script produces this output:
ERROR: System.Runtime.InteropServices.SEHException: External component has thrown an exception.
at cpp_lib.Date.{ctor}(Date* , Int32 , Int32 , Int32 )
at CLI_Lib.Date..ctor(Int32 year, Int32 month, Int32 day) in ...\Date.cpp:line 12
at My_Script.Program.Main(String[] args) in ...\Program.cs:line 25
The problem is, there is no mention of "Month is out of range". Is there a way to simply forward the original C++ error message to C#?
I kind of found a way to do this by tweaking cli_lib/Date.hpp like so:
namespace CLI_Lib
{
public ref class Date : public ManagedObject<cpp_lib::Date>
{
public:
Date::Date(int year, int month, int day)
: ManagedObject()
{
try
{
m_Instance = new cpp_lib::Date(year, month, day);
}
catch (const std::exception& e)
{
throw gcnew Exception(char_arr_to_csharp_string(e.what()));
}
}
}
}
But this feels very clunky, and requires me to add these try-catch constructs all over the place. Is there a better way?

Related

CLR Host - how to execute functions with arbitrary method signature

I've a managed C# test DLL and I want to call its functions from my unmanaged C++ code.
The problem is that I can't find an example that shows how to call arbitrary functions from my c++ code, I could only get the ICLRRuntimeHost::ExecuteInDefaultAppDomain working, but this function does use a predefined method signature.
Question
I'm able to execuete the ShowMsg function from my C# DLL but I can't find out how to call methods with other signatures than int(int). Can someone please help me with that?
C++ Code
// interface definitions of functions from c# dll
typedef int (*TestCallbackPtr)(std::wstring info, int value1, double value2);
typedef int (*TestFunctionPtr)(std::wstring string1, int int1, double double1, TestCallbackPtr callback);
// path of my c# dll
auto pathDll = L"..."
void RunTest()
{
ICLRMetaHost* clrMetaHost = NULL;
ICLRRuntimeInfo* clrRuntimeInfo = NULL;
ICLRRuntimeHost* clrRuntimeHost = NULL;
// loading the data above is excluded for simplicity!
// ...
// -------------
// Test 1: run ShowMsg => WORKING
// -------------
DWORD pReturnValue;
HRESULT result = clrRuntimeHost->ExecuteInDefaultAppDomain(pathDll, L"Test.Managed", L"ShowMsg", L"Hello from C++", &pReturnValue);
// -------------
// Test 2: run TestFunction
// -------------
TestCallbackPtr callback = [](std::wstring info, int value1, double value2) {
return 21;
};
TestFunctionPtr function = NULL;
// how to continue? how can I execute the function here and assign my local function pointer so that following will work:
// (*function)(L"Test", 45, 1.23, callback);
}
C# Code
namespace Test
{
public class Managed
{
public static int ShowMsg(string msg)
{
MessageBox.Show(msg);
return 0;
}
public delegate int TestCallback([MarshalAs(UnmanagedType.LPWStr)] string info, int value1, double value2);
public static int TestFunction(
[MarshalAs(UnmanagedType.LPWStr)] string string1,
int int1,
double double1,
TestCallback callback)
{
string info = $"string1 = {string1} | int1 = {int1} | double1 = {double1}";
int returnValue = callback(info, 5, 10.34);
return returnValue;
}
}
}

C# enum conversion, cleanest way

Background
Having a delphi background I am used to fixing lots of thgings using constants and const arrays et all. Also Delphi allpws to have type conversions on enums using helper classes.
Now take a look at these enums from C#
public enum DateInterval { Off, Day, Month, Year };
public enum TimeInterval { Off, MilliSecond, Second, Minute, Hour };
public enum DateTimeInterval { Off, MilliSecond, Second, Minute, Hour, Day, Month, Year };
As you can see, there can be a logical conversion between thse enums, and I have managed to accomplish this using:
public static class DateIntervalHelper
{
public static DateTimeInterval ToDateTimeInterval(this TimeInterval aInterval)
{
switch (aInterval)
{
case TimeInterval.MilliSecond:
return DateTimeInterval.MilliSecond;
case TimeInterval.Second:
return DateTimeInterval.Second;
case TimeInterval.Hour:
return DateTimeInterval.Hour;
default: // ivOff
return DateTimeInterval.Off;
}
}
public static DateTimeInterval ToDateTimeInterval(this DateInterval aInterval)
{
switch (aInterval)
{
case DateInterval.Day:
return DateTimeInterval.Day;
case DateInterval.Month:
return DateTimeInterval.Month;
case DateInterval.Year:
return DateTimeInterval.Year;
default: // ivOff
return DateTimeInterval.Off;
}
}
}
In delphi I would rather do something like this
const
cDate2DateTimeInterval:array[DateInterval] of DateTimeInterval=(
DateTimeInterval.Off,
DateTimeInterval.Day,
DateTimeInterval.Month,
DateTimeInterval.Year);
cTime2DateTimeInterval:array[TimeInterval] of DateTimeInterval=(
DateTimeInterval.Off,
DateTimeInterval.MilliSecond,
DateTimeInterval.Second,
DateTimeInterval.Minute,
DateTimeInterval.Hour);
And then use these arrays to "map" the conversion. (Maybe some Snytax™ errors, but you will get the point)
Question
Wat would be a cleaner way to implement this conversion in C#, using Core3.1 ?
This may not be the most glamorous solution but I think it has the array/map kind of feel you're talking about. Use a dictionary that maps one type to another. You could create another dictionary to go backwards by flipping the types around. Usage is shown in the "Test" method below.
public static Dictionary<DateInterval, DateTimeInterval> DateToDateTime = new Dictionary<DateInterval, DateTimeInterval>()
{
{ DateInterval.Off, DateTimeInterval.Off},
{ DateInterval.Day, DateTimeInterval.Day},
{ DateInterval.Month, DateTimeInterval.Month},
{ DateInterval.Year, DateTimeInterval.Year}
};
public static void Test()
{
//This acts kind of like an array/map
DateTimeInterval converted = DateToDateTime[DateInterval.Day];
}
Since the name is identical you could do the following
public static class DateIntervalHelper
{
public static DateTimeInterval ToDateTimeInterval(this TimeInterval aInterval)
{
if (Enum.TryParse<DateTimeInterval>(aInterval.ToString(), out var #enum))
return #enum;
return DateTimeInterval.Off;
}
public static DateTimeInterval ToDateTimeInterval(this DateInterval aInterval)
{
if (Enum.TryParse<DateTimeInterval>(aInterval.ToString(), out var #enum))
return #enum;
return DateTimeInterval.Off;
}
}
Example:
https://dotnetfiddle.net/bXlBfW
A real nice way is to use the recently introduced EnumConverter. It's used to convert from any Enum to any other type (also another Enum). EnumConverter inherits the well known TypeConverter.
You can check the documentations here: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.enumconverter?view=netcore-3.1
Sample copy-pasted from the docs:
Enum myServer = Servers.Exchange;
string myServerString = "BizTalk";
Console.WriteLine(TypeDescriptor.GetConverter(myServer).ConvertTo(myServer, typeof(string)));
Console.WriteLine(TypeDescriptor.GetConverter(myServer).ConvertFrom(myServerString));
Of course you could go the manual way, build up a Dictionary and return the the value for each key in a helper class.
The big advantage of the type/enum converter is the following: If you have some place in your code where you don't know the origin type or the target type, you can get a converter using TypeDescriptor.GetConverter and convert <U> to <T>.
You could write a generic converter:
static bool TryConvert<TSourceEnum, TDestEnum>(TSourceEnum source, out TDestEnum result)
where TSourceEnum : struct, Enum
where TDestEnum : struct, Enum
{
if (Enum.TryParse(source.ToString(), out TDestEnum r))
{
result = r;
return true;
}
result = default;
return false;
}
Usage:
if (TryConvert(DateInterval.Off, out TimeInterval timeInterval))
{
// Do something with your time interval
}
Here the string representation of the source enum value is parsed into a destination enum value.
Based on Kd's answer, and still using the delphistyle mapping arrays approach (I can because because my enums are contigous) I came up with this :
public enum DateInterval { Off, Day, Month, Year };
public enum TimeInterval { Off, MilliSecond, Second, Minute, Hour };
public enum DateTimeInterval { Off, MilliSecond, Second, Minute, Hour, Day, Month, Year };
public static class DateIntervalHelper
{
private static readonly DateTimeInterval[] dateIntervalMap =
{
DateTimeInterval.Off, DateTimeInterval.Day, DateTimeInterval.Month, DateTimeInterval.Year
};
private static readonly DateTimeInterval[] timeIntervalMap =
{
DateTimeInterval.Off, DateTimeInterval.MilliSecond, DateTimeInterval.Second, DateTimeInterval.Minute, DateTimeInterval.Hour
};
public static DateTimeInterval ToDateTimeInterval(this TimeInterval aInterval)
=> timeIntervalMap[(int)aInterval];
public static DateTimeInterval ToDateTimeInterval(this DateInterval aInterval)
=> dateIntervalMap[(int)aInterval];
}
I actually tested this and can confirm it works :>
Using this is also easy, check overloaded functions at the end of the source:
public static DateTime StartOfInterval(DateTime aInput, DateTimeInterval aInterval)
{
switch (aInterval)
{
case DateTimeInterval.MilliSecond:
return new DateTime(aInput.Year, aInput.Month, aInput.Day, aInput.Hour, aInput.Minute, aInput.Second, aInput.Millisecond);
case DateTimeInterval.Second:
return new DateTime(aInput.Year, aInput.Month, aInput.Day, aInput.Hour, aInput.Minute, aInput.Second, 0);
case DateTimeInterval.Minute:
return new DateTime(aInput.Year, aInput.Month, aInput.Day, aInput.Hour, aInput.Minute, 0, 0);
case DateTimeInterval.Hour:
return aInput.BeginOfHour();
case DateTimeInterval.Day:
return aInput.BeginOfDay();
case DateTimeInterval.Month:
return aInput.BeginOfMonth();
case DateTimeInterval.Year:
return aInput.BeginOfYear();
default: // ivOff
return aInput;
}
}
public static DateTime StartOfInterval(DateTime aInput, DateInterval aInterval)
{
return StartOfInterval(aInput, aInterval.ToDateTimeInterval());
}
public static DateTime StartOfInterval(DateTime aInput, TimeInterval aInterval)
{
return StartOfInterval(aInput, aInterval.ToDateTimeInterval());
}

How to get an instance from a factory based on a parameter

I have an interface
public interface ITaxService
{
public DateTime ValidFrom { get; }
public DateTime ValidUntil { get; }
public decimal GetTax(decimal taxableAmount);
}
I have some concrete classes the implement this interface
class TaxService2019 : ITaxService
{
public DateTime ValidFrom { get { return new DateTime(2019, 1, 1); } }
public DateTime ValidUntil { get { return new DateTime(2019, 12, 31); } }
public decimal GetTax(decimal taxableAmount)
{
throw new NotImplementedException();
}
}
class TaxService2020 : ITaxService
{
public DateTime ValidFrom { get { return new DateTime(2020, 1, 1); } }
public DateTime ValidUntil { get { return new DateTime(2020, 12, 31); } }
public decimal GetTax(decimal taxableAmount)
{
throw new NotImplementedException();
}
}
Finally I have a factory which gets me the right service based on a date.
public static class Factory
{
static List<Type> TaxTypes;
static Factory()
{
RegisterTypes();
}
private static void RegisterTypes()
{
TaxTypes = new List<Type>();
TaxTypes.Add(typeof(TaxService2019));
TaxTypes.Add(typeof(TaxService2020));
}
public ITaxService GetTaxService(DateTime legalDate)
{
// What to put here?
}
Now I can't decide how to elegantly get the right service. I have tried 3 things:
Else-if list for every date range
if(new DateTime(2019,1,1) <= legalDate && legalDate <= new DateTime(2019,12,31))
{
return new TaxService2019();
}
etc...
Seems bad to maintain.
Loop over every type in the list, instantiating them, read the dates and then compare them to return the right instance. Worst case I have instantiated n classes to find 1.
Seems to have bad performance (very resource/time hungry)
Use some reflections. In this case I would add public static DateTimes to the concrete classes. Value would then be retrieved via type.GetProperty("date").GetValue(type). The problem here would be that now there are 2 properties that return the same value + there is no way I can enforce someone to put the static variables in the concrete classes. This seems like the ugliest of possibilities.
How would you elegantly solve this problem?
You can do something like this, for example:
public static class Factory
{
static List<(ITaxService service, Func<ITaxService> factory)> TaxDesc;
static Factory()
{
RegisterTypes();
}
private static void RegisterTypes()
{
TaxDesc = new List<(ITaxService service, Func<ITaxService> factory)>
{
(new TaxService2019(), () => new TaxService2019())
};
}
public static ITaxService GetTaxService(DateTime legalDate)
{
foreach (var taxDesc in TaxDesc)
{
if(taxDesc.service.ValidFrom <= legalDate && legalDate < taxDesc.service.ValidUntil)
{
return taxDesc.factory();
}
}
throw new Exception();
}
}
Better option can be using DI container, many of them allow multiple registrations for interface and allow resolving collection of interface, so you will not need to bother about the parameter injection into TaxService if needed.

CS1519 Error within string calculator kata tdd

I have been working on some TDD Kata and have it nearly finished but was running into the CS1519 error. VS is telling me where the error is occurring but not sure how to go about fixing the solution. This is my first crack at doing TDD of any kind and looking for some general pointers as well.
I've been on stack overflow looking at other threads about talking about CS1519 but none of them (that I could find) seem to answer my exact question. Also, have checked out Stack Exchange for specific TDD Kata questions but needing more explanation.
using System;
using System.Linq;
namespace ChallengeCode
{
public class Calculator
{
public int Main(string number)
{
TestUnits();
if (string.IsNullOrEmpty(number))
return 0;
var numberArray = number.Replace('\n', ',').Split(',');
numberArray = NewMethod(numberArray);
NonNegValidate(numberArray);
var numberArrayInt = numberArray.Select(x => int.Parse(x)).ToArray();
return numberArrayInt.Where(x => x <= 1000).Sum(x => x);
}
private static void TestUnits()
{
throw new NotImplementedException();
}
internal static double Add(string number)
{
throw new NotImplementedException();
}
private static string[] NewMethod(string[] numberArray)
{
if (numberArray[0].StartsWith("//"))
{
var delimiter = Convert.ToChar(numberArray[0].Remove(0, 2).Distinct());
foreach (var delimiters in numberArray)
{
numberArray[1] = numberArray[1].Replace(delimiter, ',');
}
numberArray = numberArray[1].Split(',');
}
return numberArray;
}
private static void NonNegValidate(string[] numberArray)
{
if (numberArray.Any(x => int.Parse(x) < 0))
throw new Exception($"negatives not allowed {string.Join(" ", numberArray.Where(x => int.Parse(x) < 0))}");
}
}
}
Here is the code that I am using for Program.cs
using NUnit.Framework;
using System;
namespace ChallengeCode
{
class TestUnits
{
public void Add_Number_ReturnsSum(int expected, string number)
{
Assert.AreEqual(expected, Calculator.Add(number));
}
public void Add_NegNumber_Throw_An_Exception()
{
const string number = "1\n-2,-3";
var exception = Assert.Throws<Exception>(() => Calculator.Add(number));
Assert.AreEqual("negatives not allowed -2 -3", exception.Message);
}
}
}
More Details:
Screenshot of Code
I think your entry point is expecting an array instead of a single string.
I changed your code to this and it compiled. This is using the first element of your array... but has separate exceptions.
static int Main(string[] number)
{
TestUnits();
if (string.IsNullOrEmpty(number[0]))
return 0;
var numberArray = number[0].Replace('\n', ',')
.Split(',');
}
it's throwing more exceptions for being outside the bounds of this array because I'm not aware of what context this program is being used in. I assume you have another program feeding the string parameter into this one?
The Main method can be declared with or without a string[] parameter that contains command-line arguments.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/main-and-command-args/

Is there a structure that handles Timeblocks?

I want to handle Timeblocks, that means a set of two DateTimes which represent for example the presence of employees. Is there already any structure that i can use to search for a block before or after a specific time?
There are many ways i can imagine to express the situation, like i said with two DateTimes for start and end or with a Datetime for start and a TimeSpan. But i want them to be handled in a kind of Collection. So is there anything similar that i can use or do i have to implement it completely on my own?
Thanks
this library is a great thing - may you get inspired
Time Period Library for .NET
The class:
public class TimePeriod
{
public DateTime Oldest { get; set; }
public DateTime Newest { get; set; }
public TimePeriod(DateTime oldest, DateTime newest)
{
Oldest = oldest;
Newest = newest;
}
public bool Contains (DateTime time)
{
return Oldest.CompareTo(time) <= 0 && Newest.CompareTo(time) >= 0;
}
public bool IsAfter(DateTime time)
{
return Newest.CompareTo(time) <= 0;
}
public bool IsBefore(DateTime time)
{
return Oldest.CompareTo(time) >= 0;
}
}
The Test:
class Program
{
static void Main(string[] args)
{
var period = new TimePeriod(
DateTime.Now.AddDays(-2),
DateTime.Now.AddDays(1));
var date = DateTime.Now;
var contains = period.Contains(date); // true
var isBefore = period.IsBefore(date); // false
var isAfter = period.IsAfter(date); // false
date = DateTime.Now.AddDays(-10);
contains = period.Contains(date); // false
isBefore = period.IsBefore(date); // true
isAfter = period.IsAfter(date); // false
date = DateTime.Now.AddDays(10);
contains = period.Contains(date); // false
isBefore = period.IsBefore(date); // false
isAfter = period.IsAfter(date); // true
}
}
Now you can use collections and linq with extensions methods and lambda expression to look for time blocks.
This is not built-in. If you want to implement this yourself you probably want to create a struct. This will give you value-type copy semantics. Such a value behaves just like built-in types like int or DateTime. Very intuitive to use.
You may take a look at TimeSpan. Thats a struct to handle a "Timeblock"
I've used a DateSpan structure before. You can extend is a much as one likes, but this will give you a starting point.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace StackOverFlowDateSpan
{
[StructLayout(LayoutKind.Auto)]
[Serializable]
public struct DateSpan : IComparable, IComparable<DateSpan>, IEquatable<DateSpan>
{
public DateSpan(DateTime start, DateTime end)
: this()
{
Start = start;
End = end;
}
#region Properties
public TimeSpan Duration
{
get { return TimeSpan.FromTicks((End - Start).Ticks); }
}
public DateTime End { get; private set; }
public DateTime Start { get; private set; }
#endregion
public int CompareTo(DateSpan other)
{
long otherTicks = other.Duration.Ticks;
long internalTicks = Duration.Ticks;
return internalTicks > otherTicks ? 1 : (internalTicks < otherTicks ? -1 : 0);
}
public bool Equals(DateSpan other)
{
return End.Equals(other.End) && Start.Equals(other.Start);
}
public int CompareTo(object other)
{
if (other == null)
{
return 1;
}
if (!(other is DateSpan))
{
throw new ArgumentNullException("other");
}
return CompareTo((DateSpan)other);
}
public override bool Equals(object other)
{
if (ReferenceEquals(null, other))
{
return false;
}
return other is DateSpan && Equals((DateSpan)other);
}
public override int GetHashCode()
{
unchecked
{
return (End.GetHashCode() * 397) ^ Start.GetHashCode();
}
}
public static bool operator ==(DateSpan left, DateSpan right)
{
return left.Equals(right);
}
public static bool operator !=(DateSpan left, DateSpan right)
{
return !left.Equals(right);
}
private sealed class EndStartEqualityComparer : IEqualityComparer<DateSpan>
{
#region IEqualityComparer<DateSpan> Members
public bool Equals(DateSpan x, DateSpan y)
{
return x.End.Equals(y.End) && x.Start.Equals(y.Start);
}
public int GetHashCode(DateSpan obj)
{
unchecked
{
return (obj.End.GetHashCode() * 397) ^ obj.Start.GetHashCode();
}
}
#endregion
}
private static readonly IEqualityComparer<DateSpan> _endStartComparerInstance = new EndStartEqualityComparer();
public static IEqualityComparer<DateSpan> EndStartComparer
{
get { return _endStartComparerInstance; }
}
}
}
Thanks for the help! I will tae a closer look at the TimePeriod Library and do some experiments with Linq. I already have an approch that implements binary search, so if someones interested you can write me ;)

Categories

Resources