0

I have a C program, that I would like to print its output from swift, and when it scans I can give it input through Swift. Is such thing possible? I tried this with a simple function, and it worked, but how can someone do so with many different functions that call other functions?

I know the question is a bit vague, but can someone point me into the right direction?

Example of code:

int main(int argc, char **argv) {
    int i;
    int hitme;
    char ch;
    prelim();

    if (argc > 1) { // look for -f option
        if (strcmp(argv[1], "-f")== 0) {
            coordfixed = 1;
            argc--;
            argv++;
        }
    }


    if (argc > 1) {
        fromcommandline = 1;
        line[0] = '\0';
        while (--argc > 0) {
            strcat(line, *(++argv));
            strcat(line, " ");
        }
    }
    else fromcommandline = 0;


    while (TRUE) { /* Play a game */
        setup();
        if (alldone) {
            score(0);
            alldone = 0;
        }
        else makemoves();
        skip(2);
        stars();
        skip(1);

        if (tourn && alldone) {
            printf("Do you want your score recorded?");
            if (ja()) {
                chew2();
                freeze(FALSE);
            }
        }
        printf("Do you want to play again?");
        if (!ja()) break;
    }
    skip(1);
    prout("May the Great Bird of the Galaxy roost upon your home planet.");
    return 0;
}
pouyan021
  • 177
  • 1
  • 4
  • 19
MasterWizard
  • 857
  • 2
  • 15
  • 44

4 Answers4

1

Yes.

This is extensively covered in Using Swift with Cocoa and Objective-C. Objective-C is a superset of C, so all the instructions for Objective-C work equally well for C.

The short version is that you just add the C code to your project, import its header in your Objective-C Bridging Header, and then the C functions will be available in Swift (using various automatic translations).

That said, if you really want to read the output (i.e. the results of these printf) calls, that's a bit different problem. I'd avoid it if you can. Otherwise you'd need to do something like build the C program as its own executable and use NSTask within Swift to call it and capture the output, or you'd have to hijack stdout with something like fdopen. It's a pain to do that completely correctly.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 2
    `fwopen(3)` to let the C code just call OP's functions directly could also be an option. – a3f Dec 12 '15 at 21:04
  • @rob Well basically i'm creating an old command line game in C. I want the game play to remain the same, and instead take input through a text field. So what you are saying is that, I can compile the code as an executable, and pass arguments to it, and display it on a scroll view, technically this is possible? Btw this is for iOS. However this method is not allowed in the app store, right? – MasterWizard Dec 12 '15 at 22:08
  • Correct. You can't use NSTask on App Store w/ iOS. But @a3f's `fwopen` suggestion is quite good. – Rob Napier Dec 12 '15 at 22:39
  • @a3f any guide to where I should start, tutorial or anything similar, I can't find anything! – MasterWizard Dec 13 '15 at 07:43
  • @AhmedNassar I wrote an answer in which I elaborate a little. But generally, the man page is always a good place to start. – a3f Dec 13 '15 at 08:50
1

I will focus on the second part of your question, how to interact with C code that uses the standard IO facilities:

The obvious choice as Rob Napier pointed out is just compiling the C code into an executable and using something akin to popen(3) to read and write to its standard IO facilities, the same way you would read/write any other FILE*.

Another way would be to seek out places where stdio is used and change these functions. For example you could use

#ifdef STANDALONE
#define print printf
#else
#define print passToSwift
#endif

Then you can change all the printfs to prints and just #define which mode you want your C code to operate in. In case STANDALONE is left undefined, you will have to provide a passToSwift function that will connect your C and Swift functionality.

One more way without having to change all printfs is using funopen(3) or friends, particularly fwopen(3). With fwopen(3) (man fwopen) you can provide a passToSwift function to be called whenever something is written to stdout.

#include <stdio.h>
int passToSwift(void * cookie, const char * buffer, int len)
{
    (void)cookie;
    // do stuff with the buffer you recieved
    return len;
}

int main(void)
{
    fflush(stdout);
    stdout = fwopen(NULL, passToSwift);
    printf("Hey\n");
}

The assignment to stdout is not portable, but works for me on OS X. I am not aware of any other way to achieve it. (dup2 gives EBADF for funopend streams, freopen expects an entry in the filesystem).

a3f
  • 8,517
  • 1
  • 41
  • 46
  • Generally, you should separate your "business" from your IO stuff. Had you done that, you would only need to provide new IO functions without even touching the game logic. – a3f Dec 13 '15 at 09:00
  • how can someone for example store buffer passToSwift to a variable in swift? – MasterWizard Dec 14 '15 at 18:23
1

I am adressing a quite similar problem. I have a solution open to discussion on codereview: C hack: replace printf to collect output and return complete string by using a line buffer Maybe you could use that (or a part of it) for your text game as well ...

Community
  • 1
  • 1
thgr
  • 93
  • 1
  • 8
1

The improved version of C hack: replace printf to collect output and return complete string by using a line buffer is now availabe on github as Xcode 7 project swift-C-string-passing (and standalone gcc version).

Especially look at the #define preprocessor statements to make use of the bridge to swift (similar to a3f's answer).

My solution is able to pass strings in and out to the C code. But how are the answers retrieved from the user? I.e. what does the ja() function do?

Community
  • 1
  • 1
thgr
  • 93
  • 1
  • 8