inline c function to return two values

47 views
Skip to first unread message

jjj

unread,
Sep 28, 2020, 9:10:36 PM9/28/20
to Eiffel Users
Sorry for the same-ish question again...but I can't find what I need, so...

I want to call an inline c-function that returns two values, a result and modify a basic type.  Even better, return two intergers, which in the C function is an int and the other is an int*.

Having trouble with where to put the $, &,  and *; and what they mean in relation to Eiffel.
When to use POINTER or MANAGED_POINTER; and do they even apply in this context?

calling_feature
    local
        a, b: INTEGER
        good: BOOLEAN
    do
        good := c_call (a, b)
    end

c_call (x: INTEGER; y: INTEGER): BOOLEAN
        -- Change `i' and and j and return True on success
    external
        "c inline"
    alias
    "[
        int i = 1;
        int* j =      // can't remember syntax for putting a value at an address

        x = i;        // I know this is not correct but what to do here?
        y = y;      //  ...and here?

        return (EIF_BOOLEAN) (1);       //  is that right?
    ]"
    end

Alexander Kogtenkov

unread,
Sep 29, 2020, 3:04:32 AM9/29/20
to eiffel...@googlegroups.com
Whereas the text says that one argument is an integer and the other is a pointer to an integer, the code assigns to both arguments `x` and `y`. Arguments in C (as well as in Eiffel, and most other languages) are passed by value. In other words, passing an integer does not allow changing the value of an actual argument. So, I’m using another example that demonstrates how to return an integer incremented by 1. One argument is of type INTEGER (and can be passed as an arbitrary expression on the caller side). Another argument is a pointer to an INTEGER (and it is obtained for a local variable by using an address ($) operator).
 
    calling_feature
        local

            b: INTEGER
            good: BOOLEAN
        do
            good := c_call (2 + 3, $ b)
            print (b)
        end
 
    c_call (x: INTEGER; y: TYPED_POINTER [INTEGER]): BOOLEAN
            -- Set value pointed to by `y` to the value of `x` incremented by `1` and return `True`.
        external
            "C inline"
        alias
            "[
                * $y = $x + 1;
                return EIF_TRUE;
            ]"
        end
 
`calling_feature` prints 6.
 
Alexander Kogtenkov
 
 
jjj <jjj...@uky.edu>:
 
--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/eiffel-users/8583df15-6d05-4be2-9e18-aeec4388fa34o%40googlegroups.com.
 

jjj

unread,
Sep 29, 2020, 10:47:52 AM9/29/20
to Eiffel Users
Alexander,

Thanks.  Do I understand this correctly:
1)  In the line,   good := c_call (2+3, $b)   of the calling feature, the 2nd argument means, "the address of `b'"?
2)  The line,  * $y = $x + 1 in c_call means, "the value at the address of the [Eiffel] argument `y' becomes the value of the [Eiffel] argument `x' + one"?

Okay, but 
3) how do I assign a locally declared c pointer, say  int* loc_y to `y'?
3) how do I return a locally declared c pointer, say  int*  bool as result?

jjj

Alexander Kogtenkov

unread,
Sep 29, 2020, 2:04:56 PM9/29/20
to eiffel...@googlegroups.com
> 1)  In the line,   good := c_call (2+3, $b)   of the calling feature, the 2nd argument means, "the address of `b'"?
 
Yes.
 
> 2)  The line,  * $y = $x + 1 in c_call means, "the value at the address of the [Eiffel] argument `y' becomes the value of the [Eiffel] argument `x' + one"?
 
Yes.
 
> 3) how do I assign a locally declared c pointer, say  int* loc_y to `y'?
 
You cannot. At the Eiffel side there is an integer `b`, not a pointer. As I mentioned in the previous message, arguments in most programming languages are passed "by value", i.e. if a formal argument has type X, it cannot be used to "return" a value of type X, only to get a value of type X. That’s why if a formal argument has type `int`, it cannot be used to change the value of the actual argument of type `int`. You need to pass a pointer (like `int *` in C) or a reference (sometimes specified using `in`/`out`/`ref`/`var` in other languages). In the latter case the original variable can get a new integer value. But it cannot get a new pointer value, because the pointer is still passed "by value". If you want to return a pointer to an integer using an argument, you need to pass a pointer to a pointer to an integer.
 
If this answer is not satisfactory to program your application, it would make sense to describe the original problem.
 
> 4) how do I return a locally declared c pointer, say  int*  bool as result?
 
I do not quite understand the notation `int * bool`. I can answer to the question "How to return a pointer of type `int *`?" instead. The Eiffel code would look like
 
   foo: TYPED_POINTER [INTEGER_32]
        external "C inline"
        alias "return & some_global_variable_of_type_int;"
        end
 
Hope this helps,
Alexander Kogtenkov
 
 
jjj <jjj...@uky.edu>:
 
--

You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

jjj

unread,
Sep 29, 2020, 5:21:14 PM9/29/20
to Eiffel Users
Sorry, I'm not clear.  Don't know C well enough to converse intelligently.  I understand pass by value or reference and Eiffel (and Pascal); I don't get the Eiffel-to C-to Eiffel communication.1)
Context:  for Raspberry Pi in C, I need something like

1)
     if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) { 
       
I need to obtain [not necessarily "return"] both `fd', which is type int, AND a boolean [or an int] representing success state. 

2)
     g = (uint32_t *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, $fd, $a_offset);

Again, I need `g' and a success state.

 
> 3) how do I assign a locally declared c pointer, say  int* loc_y to `y'? 
 
You cannot. At the Eiffel side there is an integer `b`, not a pointer.

I misspoke, I want to obtain the value referenced by the c pointer.
 
 
   foo: TYPED_POINTER [INTEGER_32]
        external "C inline"
        alias "return & some_global_variable_of_type_int;"
        end
 
Hope this helps,
 
Still confused with symbols $, &, *, POINTER, and TYPED_POINTER. MANAGED_POINTER.

OTOH, if you can tell me how to get the functionality "open("dev/mem")..." and the "mmap (...)" without C code, that would be even better.

jjj 
 

Larry Rix

unread,
Sep 29, 2020, 8:42:08 PM9/29/20
to Eiffel Users
Instead of returning a value, why don't you pass in the values and let the inline C make changes to the passed arguments. You can then pick those values up after the call to your inline C feature.

foo (a, b: INTEGER)
   external "C inline"
   alias " ... // sets a and b based on some C code ..."
   end

The caller of foo can then just examine a and b to see what the values are.

I know this is a sloppy way to do things and dangerous, but it does work. I seem to recall some routine in wiringPi doing this.

Larry

jjj

unread,
Sep 29, 2020, 10:46:27 PM9/29/20
to Eiffel Users
ay, there's the rub!  Arguments `a' and `b' are passed by value and can't be changed, right?
jjj

jjj

unread,
Sep 30, 2020, 1:31:10 AM9/30/20
to Eiffel Users
Here's an example showing what I want to do.  I'm trying to just test the passing of variables but can't get the syntax right or I get a segmentation violation in "c_mmap".

I am assuming that I can hold on to the address returned by mmap and pass it back in to C later.  Also assuming the values are okay even when the locals in the c-call go out of scope?

    calling feature
        local
            fd: INTEGER_32
            offset: NATURAL_32
            is_mapped: BOOLEAN
        do
            ...    -- fd is 3 at this point, and offset is something positive.
            is_mapped := c_mmap (fd, offset, $address)
        end
   
c_mmap (a_fd: INTEGER; a_offset: NATURAL_32; a_address: TYPED_POINTER [NATURAL_32]): BOOLEAN
-- Map this hardware component, setting the address to this paripheral's
-- pysical device, returning true if mapping was a success.
external
"C inline use <sys/mman.h> "
alias
"[
int b;
        int bk_size = 4 * 1024;
unsigned int* gpio;
// gpio = (uint32_t *)mmap(0, bk_size, PROT_READ|PROT_WRITE, MAP_SHARED, $a_fd, $a_offset) ;
// if (gpio == MAP_FAILED) {
// b = 0;
// } else {
// b = 1;
// }
                                      // for testing change the value in `gpio' and assign to `a_address'
                                gpio = (uint32_t *) ($a_offset + (uint32_t) (99));      //   <------- Offending line?
$a_address = gpio;                                                  //   <------- Offending line?
return (EIF_BOOLEAN) (b);
]"
end


Larry Rix

unread,
Sep 30, 2020, 6:52:43 AM9/30/20
to Eiffel Users
No—they are passed by reference and can be changed. At least, that is my recollection. I will see if I can locate the code that did that.

r...@amalasoft.com

unread,
Sep 30, 2020, 7:21:36 AM9/30/20
to eiffel...@googlegroups.com
The value being passed, in the case of an address, is the value of the address, not a reference to the address.
The address needs to be treated as a pointer on the C side because that's what it is.  It's implemented as a typed integer.
The value to which the pointer refers (not the same thing as pass-by-reference) can be modified in the usual C manner (e.g. *p = 5)
In pass-by-value, the value of the argument (the address captured by the $ operator on the Eiffel side in this example) is pushed onto the stack (via a call frame, stack frame, activation record, or whatever it's called by the compilers involved).
The argument is not passed to the C function by name or by reference, though the confusion is understandable when the 'value' itself is a kind of reference.
R
--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

r...@amalasoft.com

unread,
Sep 30, 2020, 8:10:26 AM9/30/20
to eiffel...@googlegroups.com
Though not specifically answering your question, you should consider defining, in your Eiffel code, some handy C structs the help with some of the more complex interactions.
I have a master class (ael_struct) that makes it easy to create others.  The Eiffel class representing your struct needs a size (for memory allocation) and needs features that represent the members of the struct.  This doesn't eliminate the need to map Eiffel to C, but it does help organize things a bit better.
R

Here's the text of the class (de-formatted, thanks to my email client)
>>>>>

note
description: "{
A generic wrapper for C structs
}"
system: "Part of the Amalasoft Eiffel Library."
source: "Amalasoft Corporation"
license: "Eiffel Forum V2"
copyright: "Copyright (c) 1995-2013 Amalasoft Corporation.  All Rights Reserved"
date: "See comments at bottom of class."
revision: "See comments at bottom of class."
howto: "See comments at bottom of class."
history: "See comments at bottom of class."

class AEL_STRUCT

inherit
MEMORY
redefine
default_create, dispose
end

create
default_create, make, make_from_c

--|========================================================================
feature {NONE} -- Creation and initialization
--|========================================================================

default_create
-- Create Current in its default state
-- N.B. allocates memory from heap on creation
-- Do NOT free explicitly when out of scope!!!
do
allocate_structure_memory
ensure then
valid: is_valid_address (address)
end

make
-- Create Current with newly allocated memory
-- Do NOT free when out of scope!!!
do
default_create
ensure
valid: is_valid_address (address)
end

--|------------------------------------------------------------------------

make_from_c (v: POINTER)
-- Create Current from C struct at address 'v'
-- Do NOT free when out of scope!!!
require
valid_ptr: v /= Default_pointer
do
address := v
ensure
valid: is_valid_address (address)
end

--|------------------------------------------------------------------------

make_disposable
-- Create Current with newly allocated memory
-- Ensure the allocated memory is freed on dispose
do
ok_to_free_address := True
make
ensure
valid: is_valid_address (address)
end

--|------------------------------------------------------------------------

make_disposable_from_c (v: POINTER)
-- Create Current with address 'v', presumably allocated 
-- from C, but ..
-- Ensure the allocated memory is freed on dispose
require
valid_ptr: v /= Default_pointer
do
ok_to_free_address := True
make_from_c (v)
ensure
valid: is_valid_address (address)
end

--|========================================================================
feature -- Status
--|========================================================================

address: POINTER
-- Memory address of struct

--|------------------------------------------------------------------------

struct_size: INTEGER
do
Result := c_struct_size
end

--|========================================================================
feature -- Status setting
--|========================================================================

dispose
do
if ok_to_free_address and then is_valid_address (address) then
c_free_memory (address)
end
end

--|========================================================================
feature -- Access
--|========================================================================

--|========================================================================
feature -- Comparison
--|========================================================================

--|========================================================================
feature -- Element change
--|========================================================================

--|========================================================================
feature -- Element removal
--|========================================================================

--|========================================================================
feature -- Persistence support
--|========================================================================

--|========================================================================
feature -- Serialization support
--|========================================================================

--|========================================================================
feature -- Conversion
--|========================================================================

--|========================================================================
feature -- Validation
--|========================================================================

is_valid_address (v: POINTER): BOOLEAN
-- Is 'v' a valid address? (i.e. not null)
do
Result := v /= Default_pointer
end

is_valid_size (v: INTEGER): BOOLEAN
-- Is 'v' a valid size for Current?
do
Result := v >= 4
end

--|========================================================================
feature {NONE} -- Implementation
--|========================================================================

allocate_structure_memory
-- Allocate new memory for Current
require
not_allocated: address = Default_pointer
do
address := c_allocate_memory (1, struct_size)
end

--|------------------------------------------------------------------------

copy_struct (other_struct: POINTER)
-- Copy the contents of the other structure into our allocated memory
require
valid_source: other_struct /= Default_pointer
local
pr: POINTER_REF
do
create pr
pr.set_item (address)
pr.memory_copy (other_struct, struct_size)
end

--|------------------------------------------------------------------------

deref_p (p: POINTER): POINTER
-- The value of the pointer to which point 'p' points
-- (*p)
do
Result := deref (p)
end

ok_to_free_address: BOOLEAN
-- Is it OK to free the struct pointer?

dollar_to_addr (v: POINTER): POINTER
-- Return 'v' unchanged
-- Convenience function when address expressions are not enabled
require
pointer_is_valid: v /= Default_pointer
do
Result := v
end

--|========================================================================
feature {NONE} -- Externals
--|========================================================================

deref (p: POINTER): POINTER
-- Quite literally *p
external
"C [macro  <stdio.h>] (char**): EIF_POINTER"
alias
"*"
end

--|------------------------------------------------------------------------

c_allocate_memory (num, sz: INTEGER): POINTER
-- Allocate num*sz bytes of zeroed memory
external
"C [ macro <stdio.h> ]"
alias
"calloc"
end

--|------------------------------------------------------------------------

c_free_memory (v: POINTER)
-- Free the memory bound to the already allocated pointer 'v'
require
pointer_is_valid : v /= Default_pointer
external
"C [ macro <stdio.h> ]"
alias
"free"
end

--|========================================================================
feature {NONE} -- Constants
--|========================================================================

c_struct_size: INTEGER
-- REDEFINE IN CHILD
-- The size of the structure in memory, according to the C 
-- compiler
-- Change stdio.h to the name of the file containing the struct
-- definition, and change the arg to sizeof() to be the struct
-- type name.
external
"C [ macro <stdio.h> ] : EIF_POINTER"
alias
"sizeof(char)"
ensure
valid_size: is_valid_size (Result)
end

--|--------------------------------------------------------------
invariant
  address_exists: is_valid_address (address)
  has_valid_size: is_valid_size (struct_size)

--|----------------------------------------------------------------------
--| History
--|
--| 001 26-Oct-2013
--|     Created original module from ancient older version (Eiffel 7.2)
--|----------------------------------------------------------------------
--| How-to
--|
--| This class should be inherited, with its c_struct_size feature
--| redefined to represent accurately the size of the structure in
--| bytes.  This is done by replacing "char" in the sizeof call with
--| the "struct x" for that particular struct. (use the typedefed
--| name if it has been typedefed).
--|
--| If you fail to so, you will allocate only one byte and you
--| will quickly blow up!
--|
--| To gain access to the fields of the struct, you must implement
--| separate get/set features for each field.  The first argument to
--| the feature must be the address.
--| Here are some examples from struct tm.
--|
--| c_tm_sec (p: POINTER): INTEGER
--|  external
--|    "C [struct <time.h>] (struct tm): EIF_INTEGER"
--|  alias
--|    "tm_sec"
--|  end
--|    
--| c_set_tm_sec (p: POINTER; v: INTEGER)
--|  external
--|    "C [struct <time.h>] (struct tm, int)"
--|  alias
--|    "tm_sec"
--|  end
--|    
--|----------------------------------------------------------------------

end -- class AEL_STRUCT

<<<<<
-------- Original Message --------
Subject: Re: Re[2]: [eiffel-users] inline c function to return two
values
--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

Larry Rix

unread,
Sep 30, 2020, 8:11:57 AM9/30/20
to Eiffel Users
"
The value to which the pointer refers (not the same thing as pass-by-reference) can be modified in the usual C manner (e.g. *p = 5)"

I knew I wasn't crazy! :-) (mostly)

jjj

unread,
Sep 30, 2020, 6:34:42 PM9/30/20
to Eiffel Users

On Wednesday, September 30, 2020 at 6:10:26 AM UTC-6, rfo wrote:
Though not specifically answering your question, you should consider defining, in your Eiffel code, some handy C structs the help with some of the more complex interactions.

The point is to avoid "complex interactions".  The last thing I want is more C code.  This should not be that complex.  I just want to get two values out of the C-code; one value happens to be declared in C as a pointer.  So the question is just what do I want, the value to which the pointer points or the pointer itself (i.e. that address)?  My unfamiliarity with C is making this harder than it should be.  More to follow as I investigate more.
jjj

Ulrich Windl

unread,
Oct 1, 2020, 3:20:54 AM10/1/20
to eiffel...@googlegroups.com
Hi guys!

Following this discussion I had the impression that there is a lot of discussion about "meta problems" without really addressing the issues.
Why not post some concreate examples instead of all those many messages about opinion and "not knowing"?

Or maybe in the spirit of Eiffel: Give clear definitions what and how to do it (and everything else is invalid).

Also: Trying to write C interfaces while not knowing how C works is probably not successful in the end.

Regards,
Ulrich

>>> jjj <jjj...@uky.edu> schrieb am 01.10.2020 um 00:34 in Nachricht
<4f638a63-649f-4b51...@googlegroups.com>:
> --
> You received this message because you are subscribed to the Google Groups
> "Eiffel Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to eiffel-users...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/eiffel-users/4f638a63-649f-4b51-9acc-2ac7c1
> e7804fo%40googlegroups.com.




Reply all
Reply to author
Forward
0 new messages