2

The following code is the essential part of my program and class to reproduce the failure:

using namespace System;

generic <class T>
ref class gener
{
public:
  void func1g (T value)
  {
    Console::WriteLine (value);
    int iValue = static_cast<int>(value);
    if (iValue <= 0) return;
    func2 ();
  }

  void func2 ()
  {
    T value = (T)(Object^)0;
    func1g (value);
  }
};

int main()
{
  gener<int>^ g = gcnew gener<int>;
  int iValue = 1;
  g->func1g (iValue);  // <<=== System.TypeLoadException

  return 0;
}

When calling func1g, I get a System.TypeLoadException. I just don't understand why.
Is it because func2 does not have a generic parameter?

Here's the full error message (in german, but it just says 'unhandled exception' and 'could not be loaded'; no details):
enter image description here

The equivalent code in C# works:

public class gener<T>
{
  public void func1g(T value)
  {
    Console.WriteLine(value);
    int iValue = Convert.ToInt32(value);
    if (iValue <= 0) return;
    func2();
  }

  public void func2()
  {
    T value = (T)(object)0;
    func1g(value);
  }
};

internal class Program
{
  private static void Main()
  {
    gener<int> g = new gener<int>();
    int iValue = 1;
    g.func1g(iValue);

    return;
  }
}

EDIT

I found a kind of 'workaround', see my answer below, but I don't know why this works.
I would appreciate it if someone could explain me the reason of this failure and the function of the workaround.

EDIT 2
In case you want to reproduce this: I use VS 2008 SP1.
I hope that it's not again compiler related like my last issue, although I personally expect this to be very likely...

Tobias Knauss
  • 3,361
  • 1
  • 21
  • 45
  • Does the exception happen exactly at `g->func1g (iValue);` or does it happen inside `func1g` somewhere? – crashmstr Oct 15 '15 at 12:53
  • @crashmstr: It happens exactly at this line. The function itself is not 'opened'. – Tobias Knauss Oct 15 '15 at 13:22
  • Is there anyting in the message of the exception? Might help: [What could be causing a System.TypeLoadException?](http://stackoverflow.com/questions/16086178/what-could-be-causing-a-system-typeloadexception) – crashmstr Oct 15 '15 at 13:26
  • @crashmstr: in case you haven't noticed yet: see the edits and my answer. – Tobias Knauss Oct 15 '15 at 13:57
  • 1
    `static_cast(value)` is absolutely *not* equivalent to `Convert.ToInt32(value)`. That would be `Convert::ToInt32(value)` and you should use that one here I guess. The equivalent of `static_cast(value)` in C# would be `(int)value`, which would fail to compile in the first place. Generics aren't templates :) – Lucas Trzesniewski Oct 15 '15 at 22:02
  • @LucasTrzesniewski: Thanks, I know. The C# code came 2nd, so I just needed to find a way to get a generic value into int to compare it and avoid an endless loop and stack overflow... – Tobias Knauss Oct 16 '15 at 06:21

2 Answers2

2

After some testing, I just found a solution to this specific problem:

I need to define func1g as follows:

generic <class T>
void func1g (T value)
{
  ...
}

But I don't understand why.
Both ways (the code in the question and this solution) compile fine, but the first way produces a runtime exception, see the post.

One problem is remaining:
Notice that func1g above is declared and defined at the same location!
My original code is separated into .h and .cpp like this:

generic <class T>
ref class gener
{
public:
  generic <class T>
  void func1g (T value);

  void func2 ()
  {
    T value = (T)(Object^)0;
    func1g (value);
  }
};

generic <class T>
void gener<T>::func1g (T value)
{
  Console::WriteLine (value);
  int iValue = static_cast<int>(value);
  if (iValue <= 0) return;
  func2 ();
}

Now the compiler complains about the cpp with error C2511: 'void gener<T>::func1g(T)': overloaded member function not found in 'gener<T>'

Therefore I continued playing around (it really was trial and error) and found a working solution, that I absolutely don't understand any more:

using namespace System;

generic <class T>
ref class gener
{
public:
  generic <class T2>
  void func1g (T2 value);

  void func2 ()
  {
    T value = (T)(Object^)0;
    func1g (value);
  }
};

generic <class T>
generic <class T2>
void gener<T>::func1g (T2 value)
{
  Console::WriteLine (value);
  int iValue = static_cast<int>(value);
  if (iValue <= 0) return;
  func2 ();
}

int main()
{
  gener<int>^ g = gcnew gener<int>;
  int iValue = 1;
  g->func1g (iValue);

  return 0;
}

Can someone explain this to me?
I actually didn't know that a double generic definition was possible.

Tobias Knauss
  • 3,361
  • 1
  • 21
  • 45
-1

This is C# code (BTW it's also broken in C# unless T is System.Int32 or System.Int32, you should be using default(T)), it's not reasonable in C++/CLI.

  void func2 ()
  {
      T value = (T)(object)0;
      func1g(value);
  }

Just because the C++/CLI compiler accepts the exact same code, does not mean it does the same thing!

object is a keyword in C#, I have no idea what macros you've added to C++/CLI to make it compile (normally, it won't), but the equivalent C++/CLI type is System::Object^ (note the handle).

Anyway, you just want a value-initialized item of type T, in C++ that's written as

T value{};
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Originally I used a different way to cast int to generic. When making the c# program, I adapted the c++cli program afterwards to make that part the same. But I already had written this part of the post including the code and copied this line from the wrong c# prog.; my fault, edited. I'm not using any macros. Also See my answer, it contains working code. The int32 BTW is only there to avoid an endless loop and stackoverflow. The original program differs in that part. After 3 years c++cli I know the ^ handle ;-) but thanks for the init info! – Tobias Knauss Oct 18 '15 at 06:50