Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

External (extra) files when using Inline::CPP

by cavac (Parson)
on Apr 16, 2024 at 09:52 UTC ( [id://11158886]=perlquestion: print w/replies, xml ) Need Help??

cavac has asked for the wisdom of the Perl Monks concerning the following question:

First of all, i have to admit that i feel that i should know this already, but i just don't.

Is it possible to add external cpp/h files when using Inline::CPP? I only need XS bindings for the stuff i'm inlining, the rest only needs to be accesible through the inlined CPP code.

Here is the simplest example i could tink of.

test.pl
use Inline CPP; for(1..3) { print "Perfect random number: ", cast_die(), "\n"; } __END__ __CPP__ #include "xkcd.h" int cast_die() { int rolled = xkcd_dice_roll(); return rolled; }
xkcd.h
#ifndef XKCD_DICE #define XKCD_DICE // See https://xkcd.com/221/ for why this is absolutely perfect #define FAIRLY_GENERATED_RANDOM_NUMBER 4 int xkcd_dice_roll(); #endif
xkcd.cpp
#include "xkcd.h" int xkcd_dice_roll() { return FAIRLY_GENERATED_RANDOM_NUMBER; }

Is something like this even possible with Inline::CPP?

PerlMonks XP is useless? Not anymore: XPD - Do more with your PerlMonks XP

Replies are listed 'Best First'.
Re: External (extra) files when using Inline::CPP
by syphilis (Archbishop) on Apr 16, 2024 at 11:13 UTC
    Here's one way that works for me:
    Firstly compile the cpp file into xkcd.o:
    g++ -c xkcd.cpp
    Then create a static library (named, say, libxkcd.a) from the generated xkcd.o:
    ar -rc libxkcd.a xkcd.o
    Then before the "use Inline CPP;" line in test.pl I inserted:
    use Inline CPP => Config => BUILD_NOISY => 1, INC => '-ID:/w/xkcd', LIBS => '-LD:/w/xkcd -lxkcd', ;
    The "BUILD_NOISY" directive just allows you to see the compilation during the initial run.
    The 'INC' and 'LIBS' directives allow xkcd.h and libxkcd.a to be located, as needed during the build.
    Obviously, for me both of those files were located in the D:/w/xkcd directory. For me, the script then outputs:
    Perfect random number: 4 Perfect random number: 4 Perfect random number: 4
    I think it might be important that there's an empty line or 2 at the bottom of all three test.pl, xkcd.h and xkcd.cpp files.

    Cheers,
    Rob

      Perfect, thank you!

      I ended up with doing some extra magic in BEGIN, to automate those compile steps (.cpp and .h files in same directory):

      #!/usr/bin/env perl use v5.38; use strict; use warnings; use Carp; use Cwd; use Array::Contains; my $dir; my $verbose; BEGIN { $dir = getcwd; print "Working directory: $dir\n"; $verbose = 0; if(contains('-v', \@ARGV)) { $verbose = 1; } # ---- Poor mans Makefile --- if(contains('--clean', \@ARGV)) { print "Cleaning up\n"; my $cmd = 'rm -rf _Inline xkcd.o libxkcd.a'; print "$cmd\n" if($verbose); `$cmd`; exit(0); } print "Compiling library...\n"; foreach my $cmd ('g++ -c xkcd.cpp', 'ar -rc libxkcd.a xkcd.o') { print "$cmd\n" if $verbose; `$cmd`; } # ---- Poor mans Makefile --- }; use Inline 'CPP'; use Inline CPP => Config => BUILD_NOISY => $verbose, INC => '-I' . $dir, LIBS => '-L' . $dir . ' -lxkcd', ; for(1..3) { print "XKCD Dice roll: ", cast_die(), "\n"; } __END__ __CPP__ #include "xkcd.h" int cast_die() { int rolled = xkcd_dice_roll(); return rolled; }

      Hmm, i also tested without the empty lines at the bottom of the files and it worked fine. But i dimly remember having the same (or similar) issue to yours on another project.

      PerlMonks XP is useless? Not anymore: XPD - Do more with your PerlMonks XP
        I ended up with doing some extra magic in BEGIN, to automate those compile steps

        AIUI, if you were to instead turn the project into a module (with Makefile.PL and XS file, etc.) you wouldn't need to create a library, nor would you even need to precompile the .cpp file.
        That can all be taken care of automatically by the Makefile.PL, courtesy of the OBJECT=>'$(O_FILES)' option being passed to WriteMakefile() .

        However, AFAIK, that capability has not been ported to the Inline::C and Inline::CPP build procedures.
        I'm not sure why that has not yet been attended to. Maybe it's difficult, or maybe it's just that no-one has got around to doing it.

        Cheers,
        Rob

      The MCE Sandbox project places the C and header files inside the src folder. There is just the one *.c file, everything else placed in *.h files.

      .Inline/ Where Inline::C is configured to cache C object file +s. bin/ algorithm3.pl Practical sieve based on Algorithm3 from Xuedong Luo + [1]. src/ algorithm3.c Inline::C code for algorithm3.pl. bits.h Utility functions for byte array. output.h Fast printing of primes to a file descriptor. sandbox.h Header file, includes bits.h, output.h, sprintull.h. sprintull.h Fast base10 to string conversion. typemap Type-map file for Inline::C.

      I pass -I${base_dir}/src for Inline C, inside algorithm3.pl

      BEGIN { $ENV{PERL_INLINE_DIRECTORY} = "${base_dir}/.Inline"; mkdir "${base_dir}/.Inline" unless -d "${base_dir}/.Inline"; } use Inline 'C' => Config => CCFLAGSEX => "-I${base_dir}/src -O3 -fomit-frame-pointer", TYPEMAPS => "${base_dir}/src/typemap", clean_after_build => 0; use Inline 'C' => "${base_dir}/src/algorithm3.c";

      Later editing a header file requires removing the cache .Inline folder. Inline C does not know to re-compile due to not checking the *.h time stamps.

Re: External (extra) files when using Inline::CPP
by NERDVANA (Curate) on Apr 18, 2024 at 18:00 UTC
    You can always just #include "filename.c" from the inline C code. The only reason to compile separate units is to prevent symbol collisions, or enable parallel compilation, and in a small project those aren't really problems.

    This is what I do in OpenGL::Sandbox For that matter, the C code got so large I wanted syntax highlighting, so I put it all into a separate file anyway. Note that OpenGL::Sandbox has been retooled with Inline::Module so that when installed from CPAN, it compiles once at install time like a regular XS module. But, while developing you still get the advantage of automatic recompiles each time you load it.

      You can always just #include "filename.c" from the inline C code.

      Most times, yes. But there are differences:

      If you use #include "filename.c", static functions and variables in filename.c suddenly become visible in the inline C code, whereas they are separate and not visible from inline C code if you just link with filename.o. That means you can't have one static type1 foo(...) in filename.c and another static type2 foo(...) in inline C code if you use #include "filename.c", but it works fine if you compile and filename.c to filename.o and just link with filename.o.

      Static variables are even worse. Two variables with equal name and type (one from inline C, one from filename.c) suddenly become one:

      $ cat a.c #include "b.c" static int foo; int main(int argc, char ** argv) { foo=argc; // dummy code return foo; // dummy code } $ cat b.c static int foo; $ cc -Wall -pedantic a.c $

      Note: no errors, no warnings.

      $ cat a.c #include "b.c" static int foo; int main(int argc, char ** argv) { foo=argc; // dummy code return foo; // dummy code } $ cat b.c static int foo = 42; $ cc -Wall -pedantic a.c $

      No errors, no warnings when initialized in one file.

      $ cat a.c #include "b.c" static int foo = 42; int main(int argc, char ** argv) { foo=argc; // dummy code return foo; // dummy code } $ cat b.c static int foo = 42; $ cc -Wall -pedantic a.c a.c:3:12: error: redefinition of ‘foo’ 3 | static int foo = 42; | ^~~ In file included from a.c:1: b.c:1:12: note: previous definition of ‘foo’ with type ‘int’ 1 | static int foo = 42; | ^~~ b.c:1:12: warning: ‘foo’ defined but not used [-Wunused-variable] $

      Both variables must be initialized to cause an error.

      $ cc --version cc (Debian 12.2.0-14) 12.2.0 Copyright (C) 2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There i +s NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR +POSE. $

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        That's a very nice illustration of my statement "The only reason to compile separate units is to prevent symbol collisions", but my point was that in a small enough amount of code written by one person, name collisions are not usually a problem.
      My workflow for these things is to routinely use make test (I really wish this site had Markdown so I could use ``). That does the rebuild for me. For things other than tests, make && perl -Mblib script works great.
      By the way, I'm now the maintainer of OpenGL::Modern, so if you'd like that to work differently from how it does now, please say so! I think Perl OpenGL is due somewhat of a refresh to support OpenGL versions made in the last couple of decades, so I'm very open to ideas on how to best execute that.
        I haven't really found time for any OpenGL work the last few years. My OpenGL::Sandbox has some really cool wrappers around Shaders and Programs which would be nice to have upstream for wider usability, but I can't say anything is missing really.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11158886]
Approved by marto
Front-paged by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2024-05-18 04:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found