Проблема с порядком при использовании Ruby popen2 для взаимодействия с iostreams C++

Я использую Open3 popen2 для взаимодействия с iostreams простой программы C++. Насколько я понимаю, std::cin и std::cout независимы, но порядок, в котором у меня есть вызовы чтения/записи объектов ввода-вывода моего блока popen2, кажется, имеет значение. Моя программа на С++:

int main(int argc, char** argv) {
  std::string input;
  std::cout<<"EXECUTE TASK"<<std::endl;
  std::cin>>input;
  std::cout<<"END"<<std::endl;
}

Мой рубиновый скрипт:

require 'open3'
expected_string = "EXECUTE TASK"
Open3.popen2('~/Sandbox/a.out') { |stdin, stdout|
  stdin.write('\n')
  stdin.close
  results = stdout.readlines
  puts results
}

Вышеприведенное работает нормально, но если я передвину stdout.readlines перед stdin.close, сценарий ruby ​​зависнет. Мое намерение состоит в том, чтобы условно записать \n в стандартный вывод, если программа C++ сначала записывает expected_string в стандартный вывод, но я вынужден закрыть поток стандартного ввода, прежде чем смогу выполнить строки чтения. Как я уже сказал, я понимаю, что два потока независимы, и файловые дескрипторы, возвращаемые popen2, также кажутся независимыми, так почему порядок имеет значение?

Любая помощь приветствуется. Спасибо.

Решение с полным объемом того, что я пытался выполнить (кто-то может найти это полезным):

int main(int argc, char** argv) {
    std::string input;

    std::cout<<"1"<<std::endl;
    std::cout<<"2"<<std::endl;
    std::cout<<"3"<<std::endl;
    std::cout<<"4"<<std::endl;
    std::cout<<"5"<<std::endl;
    std::cout<<"EXECUTE TASK"<<std::endl;
    std::cout.flush();
    std::cin>>input;
    std::cout<<"END"<<std::endl;
}


require 'open3'

expected_string = "EXECUTE TASK"
Open3.popen2('~/Sandbox/a.out') { |stdin, stdout|
  found = false
  begin
    while(result = stdout.readline)
      puts result
      if(result.include?(expected_string))
        found = true
        break
      end
    end
  rescue
    raise "Exception caught while reading lines"
  end
  stdin.write('\n')
  stdin.close
}

person wholevinski    schedule 25.05.2011    source источник


Ответы (2)


Похоже на тупик:

  1. Скрипт ruby ​​вызывает IO::readlines, который не вернется, пока весь поток не будет прочитан.
  2. Программа C++ фактически не завершается, пока не получит возврат каретки.

Вы можете вызвать IO::readline, который будет возвращать каждую строку по мере ее получения, или переупорядочить два сценария так, чтобы не было взаимоблокировки.

person Chris Pitman    schedule 25.05.2011
comment
Это сработало. Отредактирует решение в исходном сообщении. Спасибо. - person wholevinski; 25.05.2011

Весьма вероятно, что вам просто нужно stdout.flush после всего, что вы ожидаете от вашего корреспондента.

Поскольку канал, установленный #popen, не является tty, по умолчанию он не будет использовать буферизацию строк. Он будет выполнять блочную буферизацию. Вам нужно будет заставить ваш поток действовать в режиме дейтаграммы, вызвав #flush на границах "записи".

Кроме того, см. IO#sync=, чтобы узнать, как сбросить все операции ввода-вывода автоматически.

person DigitalRoss    schedule 25.05.2011
comment
Я не упомянул, что я уже пробовал сбрасывать перед использованием объектов ввода-вывода stdout и stdin, и он по-прежнему демонстрирует такое же поведение. Также стоит упомянуть, что если моя программа на C++ использует только один из потоков, а не cin и cout, порядок не имеет значения. - person wholevinski; 25.05.2011
comment
И C++, и Ruby-концы потока, вероятно, нуждаются в операциях сброса. - person DigitalRoss; 25.05.2011
comment
Поэтому я добавил std::cout.flush(); в C++ после моего cout и stdout.flush после stdout.readlines и все еще без кубиков. Я опубликую код выше. - person wholevinski; 25.05.2011