* Calling Ada Procedure from C - how to handle a char ptr/array @ 2011-11-18 19:40 awdorrin 2011-11-18 19:56 ` Jeffrey Carter 2011-11-19 2:16 ` Adam Beneschan 0 siblings, 2 replies; 10+ messages in thread From: awdorrin @ 2011-11-18 19:40 UTC (permalink / raw) What would be the recommended way to pass a C char pointer or char array into an Ada function or procedure that will be called from a C program? After reading through the Interfaces.C.* packages, I'm assuming I could do something like this: In my Ada procedure: procedure Get_Text( s : out Interfaces.C.char_array) is Label : String(1..8) begin Label(1..8) == "12345678"; S := Interfaces.C.To_C(Label); end; with a C program that does the following: int main() { char temp[8+1]; memset( temp, '\0', 9); adainit(); Get_Text(temp); printf("Temp is: %s\n", temp); adafinal(); } however when I compile the code I get the following warning: warning: type of argument "Get_Text.s" is unconstrained array warning: foreign caller must pass bounds explicitly Then when I run, I get an error from the following line of code: S := Interfaces.C.To_C(Label); "raised CONSTRAINT_ERROR : length check failed. What length check? Is there a better way to be doing this? Thanks! -Al ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin @ 2011-11-18 19:56 ` Jeffrey Carter 2011-11-18 20:19 ` awdorrin 2011-11-19 2:16 ` Adam Beneschan 1 sibling, 1 reply; 10+ messages in thread From: Jeffrey Carter @ 2011-11-18 19:56 UTC (permalink / raw) On 11/18/2011 12:40 PM, awdorrin wrote: > What would be the recommended way to pass a C char pointer or char > array into an Ada function or procedure that will be called from a C > program? > > After reading through the Interfaces.C.* packages, I'm assuming I > could do something like this: > > In my Ada procedure: > > procedure Get_Text( s : out Interfaces.C.char_array) is > Label : String(1..8) > begin > Label(1..8) == "12345678"; Someone's been writing C. > S := Interfaces.C.To_C(Label); > end; > > however when I compile the code I get the following warning: > warning: type of argument "Get_Text.s" is unconstrained array > warning: foreign caller must pass bounds explicitly The problem here is that Ada doesn't know the bounds of S. C has to also pass that information. > Then when I run, I get an error from the following line of code: S := > Interfaces.C.To_C(Label); > "raised CONSTRAINT_ERROR : length check failed. > > What length check? An assignment to S must have a right-hand side that's the same length as S. I suspect you'll have to assign to a slice: S (S'First .. S'First + Label'Length) := To_C (Label); > Is there a better way to be doing this? A function that returns a Char_Array might be better than an out parameter. -- Jeff Carter "Go and boil your bottoms." Monty Python & the Holy Grail 01 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 19:56 ` Jeffrey Carter @ 2011-11-18 20:19 ` awdorrin 2011-11-18 20:41 ` awdorrin 0 siblings, 1 reply; 10+ messages in thread From: awdorrin @ 2011-11-18 20:19 UTC (permalink / raw) Ok - so the warnings are just a reminder that both sides needs to know how big the char_array will be, that makes sense. I changed the Interface.C.To_C call to: Interfaces.C_To(C(Label, s, Count, True); and that worked without the Constraint error. After looking at the source code in i-c.adb and reading your comments I know understand why the Constraint Error was being raised. Yea, I am much more a 'C Guy' than an 'Ada Guy' :) Thanks! ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 20:19 ` awdorrin @ 2011-11-18 20:41 ` awdorrin 2011-11-18 21:38 ` Adam Beneschan 2011-11-18 22:32 ` Georg Bauhaus 0 siblings, 2 replies; 10+ messages in thread From: awdorrin @ 2011-11-18 20:41 UTC (permalink / raw) Tried reworking the code into a function that returns a char_array, but got a STORAGE_ERROR with a stack overflow/erroneous memory access. function Get_Label return Interfaces.C.Char_array is Label : String(1..8); begin Label(1..8) := "12345678"; return Interfaces.C.To_C(Label); end; From the source code for To_C(), it allocates the char array, so I'm not sure why this error would be raised... ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 20:41 ` awdorrin @ 2011-11-18 21:38 ` Adam Beneschan 2011-11-18 22:32 ` Georg Bauhaus 1 sibling, 0 replies; 10+ messages in thread From: Adam Beneschan @ 2011-11-18 21:38 UTC (permalink / raw) On Nov 18, 12:41 pm, awdorrin <awdor...@gmail.com> wrote: > Tried reworking the code into a function that returns a char_array, > but got a STORAGE_ERROR with a stack overflow/erroneous memory access. > > function Get_Label return Interfaces.C.Char_array is > Label : String(1..8); > begin > Label(1..8) := "12345678"; > > return Interfaces.C.To_C(Label); > end; > > From the source code for To_C(), it allocates the char array, so I'm > not sure why this error would be raised... I just tried a small test case (written entirely in Ada) that declared this function just the way you wrote it, then called it. I compiled using GNAT. It didn't raise anything. Also, I don't see anything wrong with the function. So I suspect the problem isn't occurring in this function; it's occurring somewhere else. Are you calling Get_Label directly from C? If so, how is it declared in your C code? I don't agree with Jeff's suggestion to use a function that returns a Char_Array. The reason is that Char_Array is defined as an unconstrained array, and the language says that implementations don't have to support applying the C convention to functions that return unconstrained arrays. When I try this (writing a source that defines Get_Label as above, and applying Export(C) to it), GNAT gives a warning "foreign convention function Get_Label should not return unconstrained array". Are you using GNAT? If so, do you get the same warning? If so, that's a good indication that things aren't going to work. When an Ada function returns an unconstrained array, it has to arrange to return the bounds as well as the array data, and how it does that will vary from one implementation to another. Our compiler, for example, treats this kind of function as a procedure with an implicit OUT parameter that points to a record that will contain the data pointer and the bounds, which the caller will interpret as the function result. I don't know how GNAT handles it. Regardless, it will probably not be the way that a C program will expect, so you're likely to get mismatched parameters or something else that could easily lead to a bad memory access. From the warning you described in your original post, and from my experience, I don't think GNAT handles an *unconstrained* char_array as an OUT parameter. But there are a couple ways to get around this. One is to declare your own constrained subtype: subtype constrained_char_array is Interfaces.C.Char_Array (Interfaces.C.Size_T); which declares a constrained array whose bounds are the entire range of Size_T. Of course, those won't be the actual bounds, but it will make GNAT think those are the actual bounds so that it doesn't complain about bounds not being passed in, and it doesn't try to check indexes against some bogus bound and get a Constraint_Error. procedure Get_Text (s : out constrained_char_array) is ... s := Interfaces.C.To_C (Label); -- WRONG This is wrong, because it will try to assign to all of s from indexes Size_T'First to Size_T'Last. Remember, that's what it thinks the bounds are. (In your original attempt, it had no idea what the bounds were, so assigning "s :=" couldn't have worked either.) Since the C code isn't going to pass any bound information to Get_Text, you have to help it by telling it what index range you're setting: s(Size_T'First .. Size_T'First + Label'Length) := Interfaces.C.To_C (Label); The length of the left-hand slice is Label'Length + 1, but that's what you want because To_C will stick a NUL character on the end of the string. Of course, if the character array in your C code isn't large enough to handle this result, you will be screwed, but that's par for the course in C. It might be better to have the C function pass in the sizeof() of the array as an extra parameter, which is how it's done in some of the Unix system calls. Then the Ada code could check to make sure that Label'Length + 1 <= [the Size parameter], or raise Constraint_Error if not. Another possibility is to use chars_ptr and Update in the Interfaces.C.Strings package. I'll let you look those up. Hope this helps, -- Adam ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 20:41 ` awdorrin 2011-11-18 21:38 ` Adam Beneschan @ 2011-11-18 22:32 ` Georg Bauhaus 1 sibling, 0 replies; 10+ messages in thread From: Georg Bauhaus @ 2011-11-18 22:32 UTC (permalink / raw) On 18.11.11 21:41, awdorrin wrote: > function Get_Label return Interfaces.C.Char_array is > Label : String(1..8); > begin > Label(1..8) := "12345678"; > > return Interfaces.C.To_C(Label); > end; While only partly following GNAT's advice to not have Get_Label return an unconstrained array (not following because this requires defining a constrained subtype somewhere and I was lazy), this works for me. It works with or without defining a constrained Result array in Get_Label. with Interfaces.C; function Get_Label return Interfaces.C.Char_Array; pragma Export (C, Get_Label, "get_label"); function Get_Label return Interfaces.C.Char_array is Label : String(1..8); Result : Interfaces.C.char_array(0..8); begin Label(1..8) := "12345678"; Result := Interfaces.C.To_C(Label); return Result; end; #include <stdio.h> int main() { extern char* get_label(); extern void adainit(void); extern void adafinal(void); char* label; adainit(); label = get_label(); adafinal(); fputs(label, stdout); return 0; } $ gnatmake -c -gnatwa -fstack-check -gnato -f get_label.adb gcc -c -gnatwa -fstack-check -gnato get_label.adb get_label.ads:2:10: warning: foreign convention function "Get_Label" should not return unconstrained array $ cc -W -c with_label.c $ gnatbind -n get_label.ali $ gnatlink get_label.ali with_label.o -o test_get_label $ ./test_get_label 12345678$ ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin 2011-11-18 19:56 ` Jeffrey Carter @ 2011-11-19 2:16 ` Adam Beneschan 2011-11-21 16:33 ` awdorrin 1 sibling, 1 reply; 10+ messages in thread From: Adam Beneschan @ 2011-11-19 2:16 UTC (permalink / raw) On Nov 18, 11:40 am, awdorrin <awdor...@gmail.com> wrote: > What would be the recommended way to pass a C char pointer or char > array into an Ada function or procedure that will be called from a C > program? > > After reading through the Interfaces.C.* packages, I'm assuming I > could do something like this: > > In my Ada procedure: > > procedure Get_Text( s : out Interfaces.C.char_array) is > Label : String(1..8) > begin > Label(1..8) == "12345678"; > S := Interfaces.C.To_C(Label); > end; Since you seem to be pretty new to Ada, I think you may have some misconceptions about how arrays work; so it might be useful to go over some basics. Array objects (this includes Strings) have bounds. Once the bounds are set for an object, they don't change. For Example, Label has the bounds 1..8 since you've declared it that way. You can't make Label shorter or longer by saying, for instance, Label := "123456"; This will get you a Constraint_Error. S also has bounds. Since S is a parameter with an unconstrained array type, the bounds of S are determined by whatever parameter gets passed when Get_Text is called. (That's why it's a problem when it's called from C, because C code won't know that it's supposed to give any bounds to Get_Text.) But whatever the bounds are of the parameter are, they won't change; you can't change the bounds, or make S longer or shorter, by assigning to it. That means that in your statement: S := Interfaces.C.To_C (Label); The right-hand side will be an array with 9 characters (since Label has 8, and To_C appends a NUL character). This statement will therefore work only if S's bounds are such that S will have exactly 9 characters. (I.e. if S's bounds are (1..9), (3..11), (100..108), etc.) Otherwise it will raise Constraint_Error. By the way, that applies to OUT parameters just as well as IN and IN OUT parameters. For an unconstrained array, OUT means that the data in the array will be set by the procedure. But the *bounds* of the array parameter are still input to the procedure, not output from it, and the procedure can't change them. If you need to set a portion of S, you need to use a slice on the left: S (S'First .. S'First + Label'Length) := Interfaces.C.To_C (Label); which takes advantage of the knowledge that To_C returns a string whose length is Label'Length + 1. Or there are operations in Ada.Strings.Fixed to help with this: Ada.Strings.Fixed.Overwrite (S, S'First, Interfaces.C.To_C (Label)); Hope this helps, -- Adam ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-19 2:16 ` Adam Beneschan @ 2011-11-21 16:33 ` awdorrin 2011-11-22 13:20 ` awdorrin 0 siblings, 1 reply; 10+ messages in thread From: awdorrin @ 2011-11-21 16:33 UTC (permalink / raw) I've been side-tracked on another task this morning so I haven't had a chance to investigate this further, but wanted to respond with a 'Thanks' for the suggestions and the related information. My familiarity with Ada is limited - I understand the basics but when interfacing with C, I have a tendency to get myself confused - since it seems that a lot of the 'constraint' information that Ada uses is packed in different locations depending upon the compiler. The original programmers of this code made some assumptions that do appears to be entirely compiler dependent and are not portable moving to GNAT. It would seem that modifying my approach to use the Interfaces.C.strings and char_ptr would make more sense. Thanks! ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-21 16:33 ` awdorrin @ 2011-11-22 13:20 ` awdorrin 2011-11-22 13:28 ` Simon Wright 0 siblings, 1 reply; 10+ messages in thread From: awdorrin @ 2011-11-22 13:20 UTC (permalink / raw) Had a chance to review the Interfaces.C.Strings package and I think that the Chars_Ptr will work out a lot better for what I need than passing around char_array types. The Interfaces.C.Strings package looks like it can replace the custom code that was written back in the early 90s for this project, with some very minor adaptations. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Calling Ada Procedure from C - how to handle a char ptr/array 2011-11-22 13:20 ` awdorrin @ 2011-11-22 13:28 ` Simon Wright 0 siblings, 0 replies; 10+ messages in thread From: Simon Wright @ 2011-11-22 13:28 UTC (permalink / raw) awdorrin <awdorrin@gmail.com> writes: > Had a chance to review the Interfaces.C.Strings package and I think > that the Chars_Ptr will work out a lot better for what I need than > passing around char_array types. > > The Interfaces.C.Strings package looks like it can replace the custom > code that was written back in the early 90s for this project, with > some very minor adaptations. That's good. It seems likely that that's exactly why Interfaces.C.* were written! ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2011-11-22 13:28 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2011-11-18 19:40 Calling Ada Procedure from C - how to handle a char ptr/array awdorrin 2011-11-18 19:56 ` Jeffrey Carter 2011-11-18 20:19 ` awdorrin 2011-11-18 20:41 ` awdorrin 2011-11-18 21:38 ` Adam Beneschan 2011-11-18 22:32 ` Georg Bauhaus 2011-11-19 2:16 ` Adam Beneschan 2011-11-21 16:33 ` awdorrin 2011-11-22 13:20 ` awdorrin 2011-11-22 13:28 ` Simon Wright
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox