I have a c++ program which performs one function. It loads a large data-file into an array, receives an array of integers and performs a lookup in that array, returning a single integer. I am currently calling the program with each integer as an argument, like so:
$ ./myprogram 1 2 3 4 5 6 7
I also have a ruby script, and I would like this script to utilize the c++ program. Currently, I am doing this like so.
Ruby Code:
arguments = "1 2 3 4 5 6 7"
an_integer = %x{ ./myprogram #{arguemnts} }
puts "The program returned #{an_integer}" #=> The program returned 2283
This is all working properly, but my problem is that each time ruby makes this call, the c++ program has to reload the data-file (which is over 100mb) - very slow, and very inefficient.
How can I rewrite my c++ program load the file only once, allowing me to make many lookups via a ruby script without reloading the file each time. Would using sockets be a sensible approach? Writing the c++ program as a ruby extension?
Obviously I am not an experienced c++ programmer, so thanks for your help.
A possible approach is to modify your C++ program so that it takes its input from the standard input stream (std::cin) instead of from the command line parameters, and returns its result through the standard ouput (std::cout) instead of as main's return value. Your Ruby script would then use popen to launch the C++ program.
Assuming the C++ program currently looks like:
// *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;
}
It would be transformed into something like:
// *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;
}
And the Ruby script would look like
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()
Well,
You could go about this a number of different ways.
1) A simple, potentially ugly way to do this is to have your c++ run and intermittently check for a file, have your ruby script produce said file containing your arguments. Your C++ program would then use the contained arguments returning it's result to a result file which you could wait on within your ruby script... This is obviously HACK TASTIC but it's uber simple to implement and would work.
2) Expose your c++ code as a c extension to ruby. This is not as hard as it's sounds especially if you use RICE and would provide significantly less hackie solution.
3) If your c++ can be exposed through a c header file then it's almost trivial to construct a bridge using FFI. Jeremy Hinegardner gave a good lecture on constructing FFI interfaces at rubyconf heres the screencast
4) D-Bus provides an application communication bus, you could alter your C++ app to take advantage of said event bus and pass messages from your ruby using ruby-dbus
There are of course a thousand other routes... Maybe one or the other of these could prove viable :)
Cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With