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?
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 | [reply] [d/l] [select] |
|
#!/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.
| [reply] [d/l] |
|
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
| [reply] [d/l] |
|
|
|
|
.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. | [reply] [d/l] [select] |
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. | [reply] [d/l] |
|
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". ;-)
| [reply] [d/l] [select] |
|
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.
| [reply] |
|
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.
| [reply] [d/l] [select] |
|
| [reply] |
|
|
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.
| [reply] |
|
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.
| [reply] |
|
|