Agnel Kurian
Agnel Kurian

Reputation: 59496

Using array<> in generic function

In my C++/CLI code, I have about 20 different classes. I have overloaded a print function 20 times to handle an instance of each class. Now I need to additionally support arrays of objects of each of those 20 classes and hate to have to write another 20 overloads which are mostly verbatim copies of each other. As an example, see below:

void print(int i){
  Console::WriteLine("Integer: {0}", i);
}

void print(String ^s){
  Console::WriteLine(L"Hello " + s);
}

generic <typename T>
void print(array<T> ^ts){
  for(int i = 0, n = ts->Length; i < n; ++i)
    print(ts[i]);
}

int main(array<System::String ^> ^args)
{
  array<String^> ^s = gcnew array<String^>{ L"apple", L"ball", L"car" };
  print(s);
  Console::WriteLine(L"Hello World");
  return 0;
}

But the above results in the following error:

error C2665: 'print' : none of the 2 overloads could convert all the argument types

Why doesn't this compile? What is the alternative for what I am trying to do?

Upvotes: 1

Views: 876

Answers (2)

Tom Blodget
Tom Blodget

Reputation: 20812

The reason it can't be compiled is that there is no print<T> function. The only print functions are for int and String.

A C++ template will work:

template <class T>
void print(array<T> ^ts){
  for(int i = 0, n = ts->Length; i < n; ++i)
    print(ts[i]);
}

Upvotes: 2

David Yaw
David Yaw

Reputation: 27874

The reason it doesn't compile is that generic methods, unlike template methods, exist for all types possible. Template methods are created just when they are called. Since you're only calling print with a string array, a template method definition would compile a version that explicitly takes a string array, and nothing else. It would be implemented to call the print(String^) method, and there'd be no errors.

Since it's generic, there's only one compiled version of the method. If you pass a T to another method, there has to be an overload of that method that can take any parameter, even ones you're not using in your program. For this method, if you're going to pass a T to a method, it has to take a parameter of type Object^.

To make this work, I'd implement print so that it takes an Object, and use reflection to find the class name to print.

void print(Object^ o)
{
    Console::WriteLine("{0}: {1}", o->GetType()->Name, o);
}

generic <typename T>
void print(array<T> ^ts){
  for(int i = 0, n = ts->Length; i < n; ++i)
    print(ts[i]);
}

If you want to provide specific versions for specific types, implement some additional logic in the print method:

void print(Object^ o){
  if(o->GetType() == String::typeid)
    Console::WriteLine("Hello {0}", o);
  else
    Console::WriteLine("{0}: {1}", o->GetType()->Name, o);
}

generic <typename T>
void print(array<T> ^ts){
  for(int i = 0, n = ts->Length; i < n; ++i)
    print(ts[i]);
}

Upvotes: 1

Related Questions