Affix

 view release on metacpan or  search on metacpan

t/019_fileio.t  view on Meta::CPAN

    return fgetc(fp);
}

// Return a new FILE* created in C
DLLEXPORT FILE* c_create_tmpfile(void) {
    FILE* fp = tmpfile();
    if (fp) {
        fprintf(fp, "Content from C");
        fflush(fp);
        rewind(fp);
    }
    return fp;
}

// Identity function to test round-tripping a PerlIO pointer.
// Since we don't link against libperl here, we treat PerlIO* as void*.
DLLEXPORT void* c_perlio_identity(void* p) {
    return p;
}

// Check if FILE* is NULL (to verify failure cases)
DLLEXPORT int c_is_null_file(FILE* fp) {
    return fp == NULL;
}
END_C
    subtest 'Standard C FILE* (Affix::File)' => sub {

        # File represents the FILE struct, so Pointer[File] is FILE*
        affix $lib, 'c_write_to_file',  [ Pointer [File], String ] => Int;
        affix $lib, 'c_read_char',      [ Pointer [File] ]         => Int;
        affix $lib, 'c_create_tmpfile', []                         => Pointer [File];
        affix $lib, 'c_is_null_file',   [ Pointer [File] ]         => Int;
        #
        subtest 'Writing to a Perl filehandle from C' => sub {
            my ( $fh, $filename ) = tempfile();

            # Note: We use a real file because PerlIO_findFILE (used internally)
            # requires a valid C-level FILE* which scalar handles (\$) might not provide.
            # Turn off buffering to ensure C sees the file state immediately
            my $old_fh = select($fh);
            $| = 1;
            select($old_fh);
            my $bytes = c_write_to_file( $fh, 'Hello from C' );
            ok $bytes > 0, 'C function returned success count';
            close $fh;

            # Verify content
            open my $check, '<', $filename or die $!;
            my $content = <$check>;
            is $content, 'Hello from C', 'Data written by C appears in file';
            unlink $filename;
        };
        subtest 'Reading from a Perl filehandle in C' => sub {
            my ( $fh, $filename ) = tempfile();
            syswrite $fh, 'ABC';
            close $fh;
            open my $read_fh, '<', $filename or die $!;
            my $char_code = c_read_char($read_fh);
            is chr($char_code), 'A', 'C function read first character correctly';
            $char_code = c_read_char($read_fh);
            is chr($char_code), 'B', 'C function read second character correctly';
            close $read_fh;
            unlink $filename;
        };
        subtest 'Returning a FILE* from C to Perl' => sub {
            my $fh = c_create_tmpfile();
            ok $fh, 'Received a filehandle from C';

            # Affix returns a Glob reference for files
            is ref($fh), 'GLOB', 'Returned handle is a Glob reference';
            my $line = <$fh>;
            is $line, 'Content from C', 'Perl can read from the C-created FILE*';

            # C-created tmpfiles usually disappear on close, simply ensure no crash
            close $fh;
        };
        subtest 'Passing invalid handles' => sub {

            # Passing undef/closed handle should result in NULL on C side
            is c_is_null_file(undef), 1, 'Passing undef results in NULL FILE*';
        }
    };
    subtest 'PerlIO* Streams (Affix::PerlIO)' => sub {

        # Bind the identity function using PerlIO type
        affix $lib, 'c_perlio_identity', [ Pointer [PerlIO] ] => Pointer [PerlIO];

        # Test Roundtrip
        # Note: PerlIO* handles are generally strictly tied to the Perl layer.
        # When passed to C, we extract the PerlIO*, pass it, and wrap it in a new Glob on return.
        my ( $fh, $filename ) = tempfile();
        syswrite $fh, 'Test Data';
        seek( $fh, 0, 0 );
        my $new_fh = c_perlio_identity($fh);
        ok $new_fh, 'Received handle back from C';
        is ref($new_fh), 'GLOB', 'Returned handle is a Glob reference';

        # Since it's the same underlying stream, reading from one should advance the other
        # or at least access the same data source.
        my $line = <$new_fh>;
        is $line, 'Test Data', 'Round-tripped PerlIO handle is readable';
        close $fh;
        close $new_fh;    # Should be safe to close the wrapper
        unlink $filename;
    }
};
#
subtest complex => sub {
    my $lib = compile_ok(<<~'END_C');
        #include "std.h"
        //ext: .c

        #include <stdio.h>
        #include <string.h>

        // Define a struct that contains a file pointer
        typedef struct {
            FILE* log_file;
            int   counter;
        } Logger;



( run in 1.187 second using v1.01-cache-2.11-cpan-5a3173703d6 )