Passing a C-styled integer array to an Ada function to retrieve the first element in the array
Asked Answered
A

2

5

I am running an experiment in trying to link google test to ada95 for unit testing. I know ada has aunit and ahven, but this is just to see if this is possible and is outside of the scope of my question. I have successfully been able to do simple functions and procedures with the basic data types. The next thing I would like to try to do is similar to the following:

Here is the main.cpp file:

#include <stdio.h>
#include <gtest/gtest.h>

extern "C" {
  int firstElement(int buffer[]);
}

TEST(tryTest, checkBuffer){
   int buffer[10] = {10,1,6,4,3,2,1,3,4,6};
   ASSERT_EQ(buffer[0],firstElement(buffer));
}

int main(int argc, char ** argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

for simplicity I will just put the ads file:

Lib_test.ads

with Interfaces.C;
with Interfaces.C.Pointers;
package Lib_Test is

  function FirstElement(a: Interfaces.C.Pointers) return Interfaces.C.Int;
  pragma Export(C,FirstElement,"firstElement");
end Lib_Test;

I know in c you are passing in the pointer to the first element not the entire array for the function. That is why I tried to use Interfaces.C.Pointers for the data type but I got the following error

subtype mark required in this context
found "Pointers" declared at i-cpoint.ads:44

I have not found a good example of using other array types besides char arrays. Can someone show me how I can use Interfaces.C.Pointers for an integer array or even how I can fix this, I believe it is just my data type in the parameter of the function. I want to be able to access the c integer array in the ada function.

Thank you all!

Arlon answered 25/6, 2020 at 23:45 Comment(2)
Interfaces.C.Pointers is a generic package, not a subtype.If you don't understand the difference, then you really don't understand Ada well enough yet to be interfacing to foreign languages. Concentrate on learning the basics of the language first.Unite
The answers here are fine so long as the Ada you’re testing is designed to be called from C. Not so good for testing plain Ada (e.g., how to deal with unconstrained array indexed by Positive?)Tryma
Z
5

According to RM B.3 (70):

An Ada parameter of an array type with component type T, of any mode, is passed as a t* argument to a C function, where t is the C type corresponding to the Ada type T.

Hence, there's no need to use package Interfaces.C.Pointers. You can just use an Ada array type. A small example:

main.cpp

#include <stdio.h>
#include <gtest/gtest.h>

extern "C" {
  void testinit();
  void testfinal();
  int firstElement(int *buffer);
}

class MyTest : public ::testing::Test {
protected:

  MyTest() {
    testinit();          // Initialize the Ada library
  }

  ~MyTest() override {
    testfinal();         // Finalize the Ada library
  }
};

TEST_F(MyTest, CheckBuffer) {
  int buffer[10] = {10,1,6,4,3,2,1,3,4,6};
  ASSERT_EQ(buffer[0], firstElement(buffer));
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

test.gpr

with "libtest";

project Test is

   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("main.cpp");
   for Languages use ("C++");

   package Compiler is
      for Switches ("c++") use ("-I/usr/src/googletest/googletest/include");
   end Compiler;

   package Linker is
      for Switches ("c++") use ("-lgtest", "-lgtest_main", "-pthread", "-ltest");
   end Linker;

end Test;

lib_test.ads

with Interfaces.C;

package Lib_Test is

   package C renames Interfaces.C;
   
   type Seq is array (0 .. 9) of C.Int;
      
   function First_Element (A : Seq) return C.Int;
   pragma Export (C, First_Element, "firstElement");
   
end Lib_Test;

lib_test.adb

package body Lib_Test is
   
   -------------------
   -- First_Element --
   -------------------
   
   function First_Element (A : Seq) return C.Int is
   begin
      return A (A'First);
   end First_Element;

end Lib_Test;

libtest.gpr

library project Libtest is
   for Library_Kind use "dynamic";
   for Library_Name use "test";
   for Library_Interface use ("lib_test");
   for Library_Auto_Init use "False";
   for Library_Dir use "lib";
   for Object_Dir use "obj";
   for Source_Dirs use ("src");   
end Libtest;

output

$ ./obj/main 
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from MyTest
[ RUN      ] MyTest.CheckBuffer
[       OK ] MyTest.CheckBuffer (0 ms)
[----------] 1 test from MyTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.
Zamir answered 26/6, 2020 at 6:2 Comment(1)
Thank you for this example, made it really clear! Now to do some hard tests!!Arlon
L
2

In addition to the complete answer by @DeeDee: In the case you truly wanted only the first element, since you're receiving a pointer to int, any of the following Ada declarations should work (not tested!!):

function First_Element (A : in out C.int) return C.Int;
--  An in/out argument exported with C convention should 
--  use a pointer underneath.

function First_Element (A : aliased C.int) return C.Int;
--  Not sure this is already in Ada 95. By marking the 
--  argument aliased, it's passed by reference.

function First_Element (A : access C.int) return C.Int;
--  Explicit pointer. I don't like this one (too low level), 
--  but it's a possibility.

The key idea to take away from your question is to use the actual pointee type and then ensure some kind of pass-by-reference mode is used.

Larrigan answered 26/6, 2020 at 9:40 Comment(1)
Thank you these were great explanations I did not know aliased before till now! Time to get down to more Ada code.Arlon

© 2022 - 2024 — McMap. All rights reserved.