Function like "Generic::List<String^>^ buildList(String^ inParm)" and avoiding warning C4172: return

  • Thread starter Thread starter rsa_net_newbie
  • Start date Start date
R

rsa_net_newbie

Hi there,

I have a Managed C++ object (in a DLL) which has a method that is
defined like ...

Generic::List<String^>^ buildList(String^ inParm)

Now, when I compile it, I get "warning C4172: returning address of
local variable or temporary". In good old 'C', that would indicate
that a 'static' was missing from the declaration of the returned value.
How do I avoid this issue in Managed C++?

It does appear to work and fill the array - but I guess the data may
get garbage collected at any point and suddenly disappear.

Any help greatly appreciated.

Cheers,
Paul

(Functions below)


Calling Program
===========
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Configuration;

using XYZ.ABC;

namespace TestDriver
{
class Program
{
static void Main(string[] args)
{
tObject testObject = null;
Console.WriteLine("Start");

testObject = new tObject(7);

IList<String> returnList = new List<String>();

returnList = testObject.buildList("information");

foreach (String thisValue in returnList)
{
Console.WriteLine("List item: [{0}]", thisValue);
}

Console.WriteLine("Finish");
Console.ReadLine();
}
}
}


AND
Called Object
==========

#include "stdafx.h"
#pragma once

using namespace System;
using namespace System::Globalization;
using namespace System::Collections::Generic;
using namespace System::Collections;
using namespace System::Runtime::InteropServices;

namespace XYZ {
namespace ABC {
public ref class tObject
{
private:
static int item_count = 0;
public:
tObject(int total_items) { item_count = total_items; }
virtual ~tObject() { item_count = 0; }

Generic::List<String^>^ buildList(String^ inParm)
{
int counter;
List<String^> ResultList = gcnew List<String^>();

for(counter = 0; counter < item_count; counter++)
{
String^ Item;
Item = gcnew String("list item content " + inParm + " " + (counter
+ 1));
ResultList.Add(Item);
}

return %ResultList;
}
};
}
}
 
rsa_net_newbie said:
{
int counter;
List<String^> ResultList = gcnew List<String^>();
List said:
for(counter = 0; counter < item_count; counter++)
{
String^ Item;
Item = gcnew String("list item content " + inParm + " " + (counter
+ 1));
ResultList.Add(Item); ResultList->Add(Item);

}

return %ResultList;
return ResultList;

You can't return a reference to a temporary object that no longer exists
after the function returns.


Tom
 
Tamas has the exact correct fix. However, since this is "stack semantics"
and not actually memory on the stack, it will work reliably the way it is.
The only thing different is that with stack semantics for an IDisposable
type, the compiler calls Dispose at the end of the scope (like a using
block).
 
Ben said:
Tamas has the exact correct fix. However, since this is "stack semantics"
and not actually memory on the stack, it will work reliably the way it is.

That's a good point. As long as you have a reference to an object, it
probably won't be garbage collected. On the other hand, if the type is
IDisposable, it gets disposed before the function returns.

Hmmmm... I don't really know whether the code in the original post is
perfectly safe or not. Generic::List<T> is not IDisposable, so it won't
be disposed on return that's for sure. I don't know if the compiler
generates code that lets the GC know that that local object is about to
have a life outside of the function. It looks like there's a moment when
the local ResultList handle is not being assigned to any existing
variable, which means it might be ninja GCed. One would have to see the
raw assembly output for that. Well, that warning would be enough to
discourage me anyway.

Tom
 
Back
Top