2015-03-26 4 views
1

Это проблема, с которой я работаю весь день, я думаю, что я уменьшил ее до своей центральной проблемы, которая представляется неожиданным поведением при передаче данных в/из приложения командной строки Objective C, которое вызывается из C++ заявление.Как работает Objective C stdin/stdout при вызове из C/C++?

Выполняется только программа Objective C работает так, как ожидалось. Когда C++ Pipe (который в этом случае является «мастером», C++ вызывает исполняемый файл Objective C) вызывает сценарий C/C++, аналогичный приведенному ниже коде Objective C, все также работает так, как ожидалось.

Кроме того, если код ввода удаляется из Objective C или если программа C++ заказывает Objective C для передачи в файл (так что команда будет «./HelloWorld> dump.txt» вместо «./HelloWorld ") все работает так, как ожидалось.

Однако, когда код в представленном сильфона выполняется, C++ зависает при попытке прочитать Objective стандартный вывод языка C, с первой попытки, прежде чем какие-либо попытки чтения STDIN были сделаны Objective C.

Objective C

#import <Foundation/Foundation.h> 

void c_print(NSString* prnt) 
{ 
    printf("%s", [prnt cStringUsingEncoding:NSUTF8StringEncoding]); 
} 
void c_print_ln(NSString* prnt) 
{ 
    printf("%s\n", [prnt cStringUsingEncoding:NSUTF8StringEncoding]); 
} 
NSString* read_till(char c) 
{ 
    NSMutableString* ret = [[NSMutableString alloc] initWithString:@""]; 

    char r = getchar(); 
    while(r!=c && r!= '\0') 
    { 
     [ret appendFormat:@"%c",r]; 
     r = getchar(); 
    } 
    return ret; 
} 

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
     c_print_ln(@"Hello, World!"); 
     NSString* exmp = read_till('\n'); 
     c_print_ln([[NSString alloc] initWithFormat:@"String I read: \"%@\"",exmp]); 
    } 
    return 0; 
} 

C++ (.h файл)

#ifndef PIPE_H 
#define PIPE_H 

#include <sys/types.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <string> 
#include <iostream> 

#define PIPE_READ 0 
#define PIPE_WRITE 1 

class outsideExecutable 
{ 
private: 
    char buf[1024]; 
    bool is_good; 

    int infp, outfp; 

public: 
    outsideExecutable(char* command); 
    ~outsideExecutable(); 

    bool isGood(); 
    std::string readline(); 
    void writeline(std::string source); 

}; 
#endif 

C++ (.cpp файл)

#include "Pipe.h" 

using namespace std; 

int main() 
{ 
    cout<<"Testing Pipe"<<endl; 
    outsideExecutable* exe = new outsideExecutable((char*)"./HelloWorld"); 
    exe->readline(); 
    exe->writeline("reading example"); 
    exe->readline(); 
    delete exe; 
} 
static pid_t popen2(const char *command, int *infp, int *outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 
     return -1; 

    pid = fork(); 

    if (pid < 0) 
     return pid; 
    else if (pid == 0) 
    { 
     close(p_stdin[PIPE_WRITE]); 
     dup2(p_stdin[PIPE_READ], PIPE_READ); 
     close(p_stdout[PIPE_READ]); 
     dup2(p_stdout[PIPE_WRITE], PIPE_WRITE); 

     execl("/bin/sh", "sh", "-c", command, NULL); 
     perror("execl"); 
     exit(1); 
    } 

    if (infp == NULL) 
     close(p_stdin[PIPE_WRITE]); 
    else 
     *infp = p_stdin[PIPE_WRITE]; 

    if (outfp == NULL) 
     close(p_stdout[PIPE_READ]); 
    else 
     *outfp = p_stdout[PIPE_READ]; 

    return pid; 
} 

outsideExecutable::outsideExecutable(char* command) 
{ 
    is_good = false; 

    if (popen2(command, &infp, &outfp) <= 0) 
     return; 

    is_good = true; 
} 
outsideExecutable::~outsideExecutable() 
{ 

} 

bool outsideExecutable::isGood() 
{ 
    return is_good; 
} 
std::string outsideExecutable::readline() 
{ 
    if(!is_good) 
     return ""; 
    string ret = ""; 
    char hld; 
    read(outfp, &hld, 1); 
    while(hld!='\n' && hld!='\0') 
    { 
     ret = ret + hld; 
     read(outfp, &hld, 1); 
    } 
    cout<<"We read:"<<ret<<endl; 
    return ret; 
} 
void outsideExecutable::writeline(std::string source) 
{ 
    if(!is_good) 
     return; 
    //Do nothing 
    cout<<"Sending command: "<<source<<endl; 
    source = source+"\n"; 
    write(infp, source.c_str(), source.length()); 
} 

#endif 

У кого-нибудь есть идеи, что может быть неправильным с этим? У меня довольно много опыта работы с C/C++, и кажется, что код канала с этой стороны работает хорошо. Кажется, что это пример Objective C, который просто не играет хорошо, я никогда не видел, чтобы такой пример трубопровода терпел неудачу.

+0

Попробуйте промыть stdout после печати на нем. Может случиться так, что время выполнения obj-c устанавливает stdout для полной буферизации. – rici

+0

Хороший звонок! Вот и все! Удивительно, как работает Objective C. По моему опыту, C, C++ и C# не требуют, чтобы поток stdout для трубопроводов работал так, как ожидалось, и учитывая, что Objective C основан на C, это немного странное поведение. Благодаря! –

ответ

0

rich (https://stackoverflow.com/users/1566221/rici) просто предоставил ответ на этот вопрос в комментарии выше. Вот обновленный код Objective C, чтобы исправить это:

void c_print(NSString* prnt) 
{ 
    printf("%s", [prnt cStringUsingEncoding:NSUTF8StringEncoding]); 
    fflush(stdout); 
} 
void c_print_ln(NSString* prnt) 
{ 
    printf("%s\n", [prnt cStringUsingEncoding:NSUTF8StringEncoding]); 
    fflush(stdout); 
} 

Видимо стандартный вывод следует промыть в Objective C для трубопроводов, чтобы работать должным образом.