Return type

C

CeZaR

Hi,

How can i specify the return type of a function returning a managed array of chars.
If i try to write: "char __gc[] func()" i get an error!

How can i do that?

Thanks!
 
T

Tomas Restrepo \(MVP\)

CeZar,
Ok, thanks!
But that is really strange...

You are right it looks completely bizarre... you're not the first to be
surprised by it! :)
It does make sense if you look at it from the point of view of the compiler
and the C++ grammar, though....
 
C

CeZaR

Hi,

Tomas, can you please explain how the compiler works here.
Sounds interesting...

By the way, I'm learning Managed extensions for C++, and it's very powerfull.

Thanks!
 
I

Ioannis Vranos

Tomas said:
CeZar,




You are right it looks completely bizarre... you're not the first to be
surprised by it! :)
It does make sense if you look at it from the point of view of the compiler
and the C++ grammar, though....


I can't understand how this crap makes sense from the C++ grammar point
of view. May you expand on that?


Also I hope this will not make into C++/CLI.


The one that would make sense is __gc char* func(), but given the
current managed extensions the one the OP mentioned:


char __gc[] func()






Best regards,

Ioannis Vranos
 
T

Tomas Restrepo \(MVP\)

Hi Ioannis,
I can't understand how this crap makes sense from the C++ grammar point
of view. May you expand on that?

Well, I'm not a language lawyer.... I do remember someone from the VC team
commented on the rationale once (perhaps Brandon or Ronald?) ... maybe they
can explain it better :)
Also I hope this will not make into C++/CLI.

It doesn't, since the __gc[] syntax is gone forever!

Now, you'd just say:

array<Object^>^ GetArray()
{
.....
}

If you haven't already, you might want to check Stan Lippman's MC++->C++/CLI
migration guide at
http://msdn.microsoft.com/visualc/default.aspx?pull=/library/en-us/dnvs05/html/transguide.asp
 
I

Ioannis Vranos

Tomas said:
It doesn't, since the __gc[] syntax is gone forever!

Now, you'd just say:

array<Object^>^ GetArray()
{
....
}


That looks like some template. This is the way we denote a raw sequence
of objects in the free store?


char p __gc[] becomes array<char ^> ^p?


If you haven't already, you might want to check Stan Lippman's MC++->C++/CLI
migration guide at
http://msdn.microsoft.com/visualc/default.aspx?pull=/library/en-us/dnvs05/html/transguide.asp


Thanks for the link.






Best regards,

Ioannis Vranos
 
T

Tomas Restrepo \(MVP\)

Ioannis,
That looks like some template. This is the way we denote a raw sequence
of objects in the free store?


char p __gc[] becomes array<char ^> ^p?

It does look templatish. The new syntax for managed arrays is based on the
syntax that would be used for std::vector.
The basic syntax now is:

array<T, A>^

where T is the type of the elements in the array, and A is the rank (which
is optional and defaults to 1)
The array itself is a handle, which is why it needs a ^, and must be created
using gcnew.

Here's an example:
array<int, 2>^ ar =
{
{ 1, 2 },
{ 2, 4 },
{ 4, 6 }
};

for ( int i=0; i< ar->GetLength(0); i++)
{
for ( int j=0; j < ar->GetLength(1); j++)
Console::WriteLine(ar[i,j]);
}
 
I

Ioannis Vranos

Tomas said:
It does look templatish. The new syntax for managed arrays is based on the
syntax that would be used for std::vector.
The basic syntax now is:

array<T, A>^

where T is the type of the elements in the array, and A is the rank (which
is optional and defaults to 1)
The array itself is a handle, which is why it needs a ^, and must be created
using gcnew.

Here's an example:
array<int, 2>^ ar =
{
{ 1, 2 },
{ 2, 4 },
{ 4, 6 }
};

for ( int i=0; i< ar->GetLength(0); i++)
{
for ( int j=0; j < ar->GetLength(1); j++)
Console::WriteLine(ar[i,j]);
}


Huh? How the array is created without gcnew? Second since it was decided
to be a template, isn't it idiotic to have the handle symbol?


I am starting to get the feeling that managed extensions were more clean
than C++/CLI (which I considered that has a less clean syntax than
C++98...)!


I came across recently:

int ^h=gcnew int;


pin_ptr<int>p=&*h;



while in managed extensions it is simply:


int *h=__gc new int;

int __pin *p=h;



The new syntax has started to disappoint me.






Best regards,

Ioannis Vranos
 
T

Tomas Restrepo \(MVP\)

Ioannis,
Huh? How the array is created without gcnew?

In this case, because I'm using the shorthand initialization syntax. You
could as well say:
Second since it was decided
to be a template, isn't it idiotic to have the handle symbol?

No. An managed array instance is a ref type, and thus is allocated on the GC
heap. Thus, the variable contains a handle to the array on the GC heap.
I am starting to get the feeling that managed extensions were more clean
than C++/CLI (which I considered that has a less clean syntax than
C++98...)!

Not at all!

You should definitely read the spec or the migration guide, it will give you
a much better idea of what the entire syntax looks like. It's hard to judge
it from just a couple minor examples!
 
T

Tomas Restrepo \(MVP\)

Well, I'm not a language lawyer.... I do remember someone from the VC team
commented on the rationale once (perhaps Brandon or Ronald?) ... maybe they
can explain it better :)

Ahh, I think I do remember something.... I believe the cause for the syntax
is that it follows the C++ syntax of putting array brackets *after* the
declarator, and so it would have to go *after* the function declaration....
or something like that :)
 
I

Ioannis Vranos

Tomas said:
In this case, because I'm using the shorthand initialization syntax. You
could as well say:
ar = gcnew array<int, 2>;
and then fill the elements one by one.




No. An managed array instance is a ref type, and thus is allocated on the GC
heap. Thus, the variable contains a handle to the array on the GC heap.


However C++/CLI says we can create ref objects (look like as if they
are) in the stack.


So it would be better if we could do that like this:



array<int, 2>ar =
{
{ 1, 2 },
{ 2, 4 },
{ 4, 6 }
};



and use %ar to get its address (which in reality is in the managed heap).


Not at all!

You should definitely read the spec or the migration guide, it will give you
a much better idea of what the entire syntax looks like. It's hard to judge
it from just a couple minor examples!


I am waiting for the final document which is currently expected in
December. However I am reading the related slides and pdfs around.






Best regards,

Ioannis Vranos
 
T

Tomas Restrepo \(MVP\)

Hi Ioannis,
array<int, 2>ar =
{
{ 1, 2 },
{ 2, 4 },
{ 4, 6 }
};

Well, I won't argue that :)
and use %ar to get its address (which in reality is in the managed heap).

Well, to be fairly complete, %ar doesn't get the address, but rather creates
a tracking reference, so it's not exactly like the & (address of operator)
in this respect.
 
B

Brandon Bray [MSFT]

CeZaR said:
Tomas, can you please explain how the compiler works here.
Sounds interesting...

I'm not Tomas, but I can supply a short foray into the grammar of C++. To
begin, let's look at how the grammar forms a simply integer array. Consider
this simple array declaration at global scope:

int x[100];

Starting with translation-unit, where all C++ programs begin their parse
tree, this is the parse tree for that expression:

translation-unit
|
declaration-seq
|
declaration
|
block-declaration
|
simple-declaration
|
decl-specifier-seq init-declarator-list ;
| |
decl-specifier init-declarator
| |
type-specifier declarator
| |
simple-type-specifier direct-declarator
| |
int direct-declarator [ constant-expression ]
| |
declarator-id 100
|
identifier
|
x

The most interesting grammar productions in this tree start with declarator.
Here are those productions from the C++ standard:

declarator:
direct-declarator
ptr-operator declarator

direct-declarator:
declarator-id
direct-declarator ( parameter-declaration-clause ) cv-qualifier-seqopt
exception-specificationopt
direct-declarator [ constant-expressionopt ]
( declarator )

ptr-operator:
* cv-qualifier-seqopt
&
::blush:pt nested-name-specifier * cv-qualifier-seqopt


So, the first thing to note is that arrays come from the third production in
the direct-declarator production. Since the brackets always follow, a
direct-declarator, you can see why a simple mistake like the following
results in a gramatical syntax error:

int[100] x;

error C2143: syntax error : missing ';' before '['
error C2059: syntax error : 'constant'

So, now using Standard C++, 8.3.5/6 says "Functions shall not have a return
type of type array or function, although they may have a return type of type
pointer or reference to such things." Let's start with how you create a
reference to an array. Looking at the grammar, the ptr-operators come first,
so you might first think this is the correct syntax:

int x[100];
int &r[100] = x;

The compiler will tell you that you cannot have an array of reference
though. Looking at the parse tree for this, that makes sense (to read a type
from a grammar, you usually start from the bottom right). To fix this, you
can parenthesize the reference:

int x[100];
int (&r)[100] = x;

Now, how do we get from here to a function returning a reference to an
array. Well, again we put the array declaration at the end of the function
declaration because the grammar requires the name to come before the
brackets. Then we use the other production in direct-declarator to get the
argument list to the function, and then we name the function with
declarator-id. Then we put the & for the reference, and then the return
type. So, we might come up with the following:

int &f()[100];

With that, we ran into the same problem as before where we have an array of
int references (clearly not allowed). Given that we also have a function
returning an array. To fix that, we parenthesize again:

int (&f())[100];

And that's it. The design for standard C++ puts array returns at the end of
the function. That's what led the old syntax to do the same thing. Since CLR
arrays have a way to be returned from functions, the restriction from the
part of the standard I quoted above was lifted.

Now, I'll be the first in line to say that array declaration in C++ is
beyond difficult. I have to question other people on the compiler team on
occasion just to figure out what some code is. I'll pose a few challenges
below that demonstrate the difference in either figuring out how to write a
particular piece of code or even reading code. That will distinguish why we
chose the syntax that we did in the new design.

Hopefully, spending enough time understanding what kind of code you can
write with arrays in C++ will convince you how much more elegant the syntax
is that we chose for CLR arrays.

I'm going to cover this and other array topics in a forthcoming blog entry
(yes, I'm actually writing a new blog entry after a nine month hiatus). As a
puzzle in the meantime, here's a challenge for anyone with enough time:

For each of the following, write the syntax for accomplishing this with
standard C++ arrays and for accomplishing it with CLR arrays:

1. An array of function pointers.
2. A function pointer taking an array argument by reference.
3. A function pointer returning a reference to an array.

As a reminder, a function pointer looks like this: void (*pf)();

And for the super challenge, what is this? (Hint: use typedefs)
void (*(& x(void (*(*)[10])(void (*(&)[10])())))[10])(void (*(&)[10])());

How would this last thing be expressed using CLR arrays?

Have fun!
 
B

Brandon Bray [MSFT]

Ioannis said:
However C++/CLI says we can create ref objects (look like as if they
are) in the stack.

So it would be better if we could do that like this:

array<int, 2>ar =
{
{ 1, 2 },
{ 2, 4 },
{ 4, 6 }
};

Unfortunately, arrays do not have copy constructors or copy assignment
operators. So, the utility of a stack based array is not very high. Early in
the process for Whidbey, one of the features we were collaborating with the
CLR team was the valarray construct. Basically, these would be statically
sized arrays that were copyable like value types. That feature was cut some
time ago, but may reappear in a coming version.
 
I

Ioannis Vranos

Brandon said:
Unfortunately, arrays do not have copy constructors or copy assignment
operators.

And?


So, the utility of a stack based array is not very high.


In contrary, from a C++ point of view, the above makes more sense for a
built in template style array, rather than with free store semantics.


Early in
the process for Whidbey, one of the features we were collaborating with the
CLR team was the valarray construct. Basically, these would be statically
sized arrays that were copyable like value types. That feature was cut some
time ago, but may reappear in a coming version.



A default valarray would be nice too, however even the above would be
more elegant/make sense with stack semantics.






Best regards,

Ioannis Vranos
 
B

Ben Schwehn

Brandon said:
And for the super challenge, what is this? (Hint: use typedefs)
void (*(& x(void (*(*)[10])(void (*(&)[10])())))[10])(void (*(&)[10])());


my attempt at an solution for this after the spoiler warning
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..
spoiler
..
..
..
..
..

I believe
void (*(& x(void (*(*)[10])(void (*(&)[10])())))[10])(void *(&)[10])());

if a function declaraion of a function taking an array of function
pointer and retuning an array of function pointer like this:


I assume that this
[1]
void (*(& x2(void (*(*)[10])(void (*(&)[10])())))[10])(void *(&)[10])());

is the same as
[2]
void (*(& x2(void (*(*)[10])(void (*(&var1)[10])())))[10])(void
(*(&var2)[10])());

first with unnamed, secon with named parameters.

with

typedef void(*(&fnptr1)[10])();
typedef void (*(*fnptr2)[10])(fnptr1);


[2] becomes

void (*(& x(fnptr2))[10])(fnptr1);

which is a declaration of this function:


void (*(& func2(fnptr2))[10])(fnptr1) {
void (*ptf[10])(fnptr1);
return ptf;
}


Is this correct or at least anywhere near the solution?
 
B

Brandon Bray [MSFT]

Ben said:
Is this correct or at least anywhere near the solution?

That's right. The equivalent way of writing it with CLR arrays is as
follows:

array<void (*)(array<void (*)()>^%)>^% y(array<void (*)(array<void
(*)()>^%)>^*);
 
B

Brandon Bray [MSFT]

Ioannis said:

It's possible I'm missing something, but I really don't see the utility of
declaring an array by value when you would have to pass it around by handle
anyways. The complaint really comes down to whether there is a caret in the
written declaration or not -- there's no semantic advantages.
In contrary, from a C++ point of view, the above makes more sense for a
built in template style array, rather than with free store semantics.

We extended the syntax to allow aggregate initialization of CLR arrays. CLR
arrays are always passed by handle. In the same regard, operators work on
handles for the exact same reason. Rather than force syntax to match
automatic storage semantics (even though it doesn't make sense), we took the
approach that it should work for garbage-collection semantics.
A default valarray would be nice too, however even the above would be
more elegant/make sense with stack semantics.

There is a big difference between the capabilities of a valarray verses an
array. First, a CLR array today (i.e. the System::Array type) does not have
copy semantics, does not physically embed in types, has the capability of
being jagged, etc. Valarrays would have different properties. I can
understand the desire to make all types super flexible, but of course that
results in less useful types in the end. Engineering types for specific
purposes with good capabilities is the right thing for language designers
and library providers to do. Choosing between a selection of types is the
job of a good developer.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top