Взаимодействие Ады с асинхронным интерфейсом на C++

У меня есть задача разработать новый интерфейс для распределенного приложения (несколько процессов, работающих на одном компьютере, физически еще не распределенных), которое состоит из множества модулей графической панели, написанных на C++/OpenGL, и одного модуля, написанного на Ada.

Модули совместно используют информацию о форме параметров (фрагменты скалярной неструктурированной информации, например: целые числа, строки, числа с плавающей запятой и т. д.). Я намереваюсь разработать компонент на основе асинхронного сокета (который я буду называть в дальнейшем «Компонент интерфейса» или IC), который будет связан каждым модулем на основе библиотеки Boost::Asio с двумя простыми «клиентскими» примитивами:

Put([ParameterName], [DestinationModule], [Payload], [Type])
Get([ParameterName], [Sourcemodule], [Payload])

ParameterName: определяет уникальное имя параметра
DestinationModule/SourceModule: обращается к модулю в системе
Полезная нагрузка: фактические данные
Тип: строка или значение, определяющее тип переданного параметра

Каждый из этих примитивов обрабатывается на стороне сервера двумя соответствующими функциями (на ИС):

//No function parameters shown here as I don't know
//exactly how I'm going to do this
ProcessPutRequest()
ProcessGetRequest()

Поскольку обработка входящих команд будет запущена IC, я намерен использовать обработчики функций, которые будут переданы вышеперечисленным функциям. Таким образом, каждый программист, ответственный за каждый модуль, может обрабатывать входящие команды и выполнять необходимые преобразования типов в своем собственном коде.

Вопрос в том... возможно ли это в Аде? Я знаю, что можно импортировать функции в С++ и вызывать их из программ на Аде, но можно ли передать обработчики функций из Ады в компонент С++?

(побочный вопрос: есть ли у вас какие-либо предложения по лучшему способу реализации этого интерфейса?)


person Guarita    schedule 19.10.2011    source источник
comment
Вы имеете в виду что-нибудь под обработчиком функции, кроме того, что вы могли бы сделать в простом C, передав адрес функции обработчика? Вы, безусловно, можете написать эквивалент такой функции-обработчика на Аде. Хотя, если Ада вызывает ваш Get(), я не понимаю, зачем вам это нужно?   -  person Simon Wright    schedule 19.10.2011
comment
К сожалению, я думаю, что должен был быть более полным в своем вопросе: я хочу использовать этот интерфейс, чтобы каждый модуль моего приложения мог выполнять как клиентские, так и серверные операции. В качестве сервера модуль должен будет обрабатывать поступающие данные. Когда эти данные (параметры или сообщения) поступают, они обрабатываются компонентом интерфейса. Вопрос: как заставить модуль обрабатывать поступающие данные без опроса ИС? Идея использования обработчиков заключалась в том, чтобы вызывать их, чтобы код модуля мог вызываться всякий раз, когда сообщение поступает на IC.   -  person Guarita    schedule 19.10.2011


Ответы (5)


Не уверен, какова семантика Get(). Ожидает ли он новых данных? вернуть последние полученные данные? (в таком случае, что произойдет, если данные еще не получены?)

Предполагая схему обратного вызова и Ада 2005, вы могли бы для начала рассмотреть спецификацию вроде

generic
   type T is private;
package Interfacing is
   --  'put' not shown
   type Receiver is not null access procedure (Value : T);
   pragma Convention (C, Receiver);
   procedure Register
     (Parameter_Name : String;
      From_Module : String;
      To_Be_Received_By : Receiver);
end Interfacing;

Я предполагаю, что IC организует сортировку и список зарегистрированных обратных вызовов.

Следует помнить об одном моменте: в этой схеме процедура Receiver вызывается в контексте стороннего (не Ada) потока, что может вызвать проблемы с поддержкой задач среды выполнения Ada. Предполагая, что вы используете GNAT, вы должны посмотреть на GNAT.Threads (файл g-thread.ads во время выполнения). Вам необходимо зарегистрировать поток как можно скорее, например, перед выполнением каких-либо String операций, таких как катетерация.

person Simon Wright    schedule 20.10.2011

Кодирование связи на основе сокетов является простым на обоих языках, особенно для интерфейса, который должен передавать только скалярные значения.

Создание отдельных компонентов для модулей, соответствующих языку, будет намного проще и быстрее, чем попытки заставить работать интерфейс и связывание на разных языках. Не поймите меня неправильно, это можно сделать, но мне потребуется веская причина, чтобы заставить меня пойти по этому пути, а не кодировать отдельные простые интерфейсы обработки данных сокетов.

person Marc C    schedule 19.10.2011

Я бы не рекомендовал создавать интерфейс, который пытается комбинировать языки, используя по существу интерфейс RPC с обратными вызовами. Это будет очень сложно и может плохо взаимодействовать с механизмом задач Ады.

Вместо этого разработайте метод передачи сообщений от одного компонента к другому. Вы можете сделать это, например, через сокеты. Сообщение может представлять собой любой блок данных разумного размера на этом уровне. Затем, помимо этой возможности, реализуйте код на C++ и Ada, который может упаковывать и распаковывать полезные сообщения способом, подходящим для каждого языка.

Если у вас есть надежный интерфейс для передачи сообщений, вы можете создавать на его основе все, что захотите. Но ключ в том, что вам не нужно будет создавать ничего, что вызывает «в» другой язык.

person Greg Hewgill    schedule 19.10.2011
comment
Хм, вы говорите, что наличие унифицированного компонента не стоит того, чтобы связывать код C++ и Ada? Обратите внимание, что IC, о котором я говорю, предназначен для связи с кодом Ada, а не для вызова через RPC. - person Guarita; 19.10.2011
comment
Хорошо, думаю, я неправильно понял, когда вы сказали, что несколько процессов выполняются на одном компьютере. - person Greg Hewgill; 19.10.2011

Что касается связывания Ada с кодом C++, в gcc есть переключатель, который создает заголовки ada из файлов .h, .cpp и .c: -fdump-ada-spec и руководство с предостережениями здесь.

Мое предостережение: я не использовал его, просто знаю о нем.

person NWS    schedule 20.10.2011
comment
Еще одно (по общему признанию, довольно очевидное) предостережение заключается в том, что и C++, и Ada должны быть скомпилированы с помощью gcc. - person T.E.D.; 20.10.2011

В общем, C++ плохо работает с другими языками. Вероятно, вам придется указать обеим сторонам использовать соглашение о вызовах C, которое обычно недоступно для нестатических функций-членов.

Так что какую бы классную гимнастику вы ни захотели проделать с Boost, вероятно, придется свести к простому C-ish интерфейсу для общения с внешним миром (в данном случае, с вашим кодом на Аде).

Существует своего рода «шаблон» для подобных вещей. Возможно, у него даже есть название, но я не занимаюсь выкройками, поэтому не знаю его. Что вы делаете, так это предоставляете статическую функцию-член, которая принимает указатель на класс в качестве параметра и внутренне вызывает себя через этот указатель класса. Затем вы делаете эту статическую функцию-член (и указатель this, который вы хотите использовать для нее) доступными для вызова внешнего (не C++) кода. Я использую это все время, чтобы предоставить подпрограммам обратного вызова ОС C++-подобные интерфейсы.

class callback {
    void register () {
       // Register the callback. This usually involves somehow pointing the 
       // other side to our static do_it() routine and passing it our 
       // current "this" pointer.
    };

    void do_it(); // The member function we want callable from outside C++

    static extern "C" void do_it (callback * instance) {
        instance->do_it();
    }
};
person T.E.D.    schedule 20.10.2011