2010-02-18 9 views
4

J'ai un programme C++ qui exécute une fonction. Il charge un grand fichier de données dans un tableau, reçoit un tableau d'entiers et effectue une recherche dans ce tableau, renvoyant un seul entier. J'appelle actuellement le programme avec chaque entier comme argument, comme suit:Communication entre un script ruby ​​et un programme C++ en cours

$ ./myprogram 1 2 3 4 5 6 7 

J'ai aussi un script Ruby, et je voudrais que ce script pour utiliser le programme C++. Actuellement, je fais cela comme ça.

code Ruby:

arguments = "1 2 3 4 5 6 7" 
an_integer = %x{ ./myprogram #{arguemnts} } 
puts "The program returned #{an_integer}" #=> The program returned 2283 

Ceci est tout fonctionne correctement, mais mon problème est que chaque rubis temps fait cet appel, le C++ programme doit recharger les données fichier (qui est plus de 100 Mo) - très lent, et très inefficace.

Comment puis-je réécrire mon programme C++ chargez le fichier une seule fois, ce qui me permet de faire de nombreuses recherches via un script ruby ​​sans recharger le fichier à chaque fois. L'utilisation de sockets serait-elle une approche raisonnable? Écrire le programme C++ comme une extension ruby?

De toute évidence, je ne suis pas un programmeur expérimenté C++, alors merci pour votre aide.

Répondre

6

Une approche possible consiste à modifier votre programme C++ afin qu'il prenne son entrée du flux d'entrée standard (std :: cin) au lieu des paramètres de ligne de commande, et renvoie son résultat via la sortie standard (std :: cout) au lieu de la valeur de retour du principal. Votre script Ruby utiliserait alors popen pour lancer le programme C++.

En supposant que le programme C++ ressemble actuellement:

// *pseudo* code 
int main(int argc, char* argv[]) 
{ 
    large_data_file = expensive_operation(); 

    std::vector<int> input = as_ints(argc, argv); 
    int result = make_the_computation(large_data_file, input); 

    return result; 
} 

Il serait transformé en quelque chose comme:

// *pseudo* code 
int main(int argc, char* argv[]) 
{ 
    large_data_file = expensive_operation(); 

    std::string input_line; 
    // Read a line from standard input 
    while(std:::getline(std::cin, input_line)){ 
     std::vector<int> input = tokenize_as_ints(input_line); 
     int result = make_the_computation(large_data_file, input); 

     //Write result on standard output 
     std::cout << result << std::endl; 
    } 

    return 0; 
} 

Et le script Ruby ressemblerait

io = IO.popen("./myprogram", "rw") 
while i_have_stuff_to_compute 
    arguments = get_arguments() 
    # Write arguments on the program's input stream 
    IO.puts(arguments) 
    # Read reply from the program's output stream 
    result = IO.readline().to_i(); 
end 

io.close() 
3

Eh bien,

Vous pouvez vous y prendre de différentes façons.

1) Une façon simple et potentiellement laide de faire cela est d'avoir votre C++ exécuté et de vérifier par intermittence un fichier, demandez à votre script ruby ​​de produire ledit fichier contenant vos arguments. Votre programme C++ utiliserait alors les arguments contenus en retournant son résultat dans un fichier de résultats que vous pourriez attendre dans votre script ruby ​​... C'est évidemment HACK TASTIC mais c'est simple à implémenter et ça marcherait.

2) Exposez votre code C++ comme une extension c à ruby. Ce n'est pas aussi difficile que ça en a l'air surtout si vous utilisez RICE et que vous obtiendrez une solution nettement moins hackie.

3) Si votre C++ peut être exposé à travers un fichier d'en-tête c, il est presque trivial de construire un pont en utilisant FFI.Jeremy Hinegardner a donné une bonne conférence sur la construction d'interfaces IFF à RubyConf Heres the screencast

4) D-Bus fournit un bus de communication de l'application, vous pouvez modifier votre C++ application pour profiter dudit bus d'événements et de transmettre des messages de votre ruby ​​en utilisant ruby-dbus

Il y a bien sûr mille autres routes ... Peut-être que l'un ou l'autre d'entre eux pourrait s'avérer viable :)

Cheers!