I would like to convert a 3d model (.obj like blender) to .h file. there is a tool at github but when I run it, I got a message error :
tool:
https://github.com/HBehrens/obj2opengl/
my commend line:
C:\Users\***>perl C:\Users\***\Desktop\vuforia\obj2opengl.pl C:\
Users\***\Desktop\vuforia\cc.obj
cc.obj is an export of blender software .
error :
Can't use 'defined(#array)' (Maybe you should just omit the defined()?) at C:\Users\***\Desktop\vuforia\obj2opengl.pl line 118.
line 118 :
if(defined(#center)) { //line 118
$xcen = $center[0];
$ycen = $center[1];
$zcen = $center[2];
}
I don't know where is the problem.
my OS is windows 64 . I installed perl before .
Can't use 'defined(#array)' (Maybe you should just omit the defined()?)
This tells you that the syntax defined(#array) is not valid, and even gives a hint. All you need to do is remove the defined(). Your code would then read
if(#center) {
$xcen = $center[0];
$ycen = $center[1];
$zcen = $center[2];
}
The if evaluation forces the array into scalar context, which makes it return its number of elements. That's probably 3, or 0. If it's 0 then it's a false value and the block is skipped. 3 on the other hand is a true value and the block will be executed.
The defined(#array) syntax was deprecated from Perl.
Use of defined on aggregates (hashes and arrays) is deprecated. It used to report whether memory for that aggregate had ever been allocated. This behavior may disappear in future versions of Perl.
The version of Perl you installed is newer than the version the author of your script used, and this is a rare occasion of an incompatible change.
Related
I'm rewriting some perl/charting software in Raku but have run into an issue using the ChartDirector perl5 module (link below) via Inline::Perl5. The module is basically a perl interface to a DLL; using the module via Inline::Perl5 seems to work for method calls explicitly included in the code - but most of the method calls are executed via the autoload 'catch all' mechanism in perl5. These do not work in my raku code.
My question is can I expect this sort of application to work using Inline::Perl5? (perhaps there is no way to 'catch' these autoload'd method calls) and, if so, how to make it so.
Thanks for any pointers, suggestions.
wf
ChartDirector software (excellent graphics/charting software - have used it for nearly 2 decades with perl): https://www.advsofteng.com/index.html
Quoting verbiage (simplified), version info, and code from the start of a chartdir forum thread about this:
I'm using Inline::Perl5 which works with every other module I've tried.
I'm hitting problems reproducing the "first project" example on the chartdir site.
using chartdir 6, freebsd 12.2 (intel platform), raku 2022.04, perl 5.32.1.
#!/usr/bin/env raku
use lib:from<Perl5> '/usr/local/lib/perl5/site_perl';
use Inline::Perl5;
my $p5 = Inline::Perl5.new;
$p5.use('perlchartdir') ;
my $data = [85, 156, 179.5, 211, 123];
my $labels = ["Mon", "Tue", "Wed", "Thu", "Fri"];
my $c = $p5.invoke( 'XYChart', 'new', 250, 250);
$c.setPlotArea(30, 20, 200, 200);
$c.addBarLayer($data);
$c.xAxis().setLabels($labels);
$c.xAxis().setLabels($labels);
$c.makeChart("simplebar.png");
All seems fine (i.e., data-dumping $c after line 8 shows a large/reasonable looking structure) until line 9 where I receive "No such method setPlotArea for invocant of type XYChart". Line 10 does appear to work (no complaints) - remaining 3 lines don't (same type of error as for line 8).
And quoting some feedback that was given by Peter Kwan, the primary dev of chartdir:
I have never used Raku before. For your case, the methods that fail seem to be AUTOLOAD methods. ... I suspect may be Raku does not support Perl AUTOLOAD, so it reports undefined methods as not found instead of forwarding it to the "catch all" method. Or may be some additional things need to be imported for it to use the AUTOLOAD.
As dwarring notes in the comments below this SO question, Inline::Perl5 does support autoload (and has for 7 years), so perhaps that aspect is a red herring?
gratefully responding to p6steve's response, I am providing some additional information ..
The full output from various representations of $c (the XYChart object) is included here: https://pastebin.com/gY2ibDaM (hope it's ok to use pastebin for this (still finding my way through stackoverflow)) - the output was 600+ lines long and wasn't sure what I could usefully edit out).
to summarize though ..
dd $c returns nil (although prints out the equivalent of $c.perl (below) to stdout (not sure why))
say $c.perl returns:
XYChart.new(inline-perl5 => Inline::Perl5.new(thread-id => 1), wrapped-perl5-object => NativeCall::Types::Pointer.new(34651088744))
say $c.^methods returns:
(WHERE addHLOCLayer ACCEPTS WHY can new isa rakuseen defined getYCoor addHLOCLayer3 raku yZoneColor Numeric addLineLayer addAreaLayer DESTROY BUILDALL gist perl WHICH sink bless getYValue Str addBarLayer new_shadow_of_p5_object AT-KEY)
finally, say Dump $c (using Data::Dump module) yields about 600 lines of output (included in the pastebin output).
Hi wingfold and welcome to the raku SO tag!
I wonder what you get with dd $c; just before the line $c.setPlotArea(30, 20, 200, 200); - e.g. is $c truly an XYChart object?
If so then what does $c.^methods (the '^' indicates a meta method ... in this case you should get a list of the available methods).
Please do post the results here and hopefully that will help the diagnosis...
Thanks for the info!
Having seen the output of the $c.^methods call, it is clear that $c has no method $c.setPlotArea (reading the error message says the same - perhaps I should have given that due weight!)
I do not know the Inline::Perl5 module well, but I have seen similar issues with Inline::Python.
My experience in Python is that the target language objects only expose their "direct" methods and do not automatically pull in all the composed methods that they can perform.
My workaround has been on the lines of an "eval" style approach, something like:
$p5.run( qq[$c.setPlotArea(30, 20, 200, 200);] );
Hope this helps!
I have a monthly Script running to archive files from past month. I'm using Archive::Tar to generate the archive. How can I check if calls to the ->write method are successful?
Does the following work? I didn't manage to fail $tar->write() yet.
unless ($tar->write( $name . '.tar.xz', COMPRESS_XZ )) {
die("cant write tar"); # or any other doings instead
}
Yes, the code in your question will catch any error within ->write.
The documentation of Archive::Tar does not specify what the return value of write is (except when no argument is provided, which isn't the case here). However, looking at the code of the module, write returns undef in case of an error and a true value in case of success:
1 if it was writing to a file (this is the case for the code in your question)
the written string if it was writing in a string.
Note that if something goes wrong and Archive::Tar returns undef, then it will also print an error message (unless you set $Archive::Tar::WARN to 0 by doing $Archive::Tar::WARN = 0). If you want to do something specific depending on the error, you can access the error message using the ->error method.
I have been trying to get perl subroutine value and substitution to get the required part of string from randomips subroutine in exim.conf. However when i use string substitution i get error as follow:
Here is what I am trying to achieve
I am trying to split string by colon and get first occurrence as "interface". I'll be using second occurrence as the "helo_data.
exim.pl
sub randomhosts {
#inet = ("x.x.x.1:hostname1.domain.com","x.x.x.2:hostname2.domain.com","x.x.x.3:hostname3.domain.com"
);
return $inet[int rand($#inet+1)];
}
exim.conf
dkim_remote_smtp:
driver = smtp
interface = "${perl{randomhosts}%:*}"
helo_data = "${sender_address_domain}"
Error I get is as follow:
"failed to expand "interface" option for dkim_remote_smtp transport: missing '}' after 'perl'".
Probably the syntax.
Any help?
The code that you are trying to copy was written by someone who doesn't know much about Perl. It includes this line:
return $inet[int rand($#inet+1)];
A Perl programmer would write this as
return $inet[rand #inet];
I think there are a couple of issues here - one with your Exim syntax and one with your Perl syntax.
Exim is giving you this error:
failed to expand "interface" option for dkim_remote_smtp transport: missing '}' after 'perl'
I don't know anything about calling Perl from Exim, but this page mentions a syntax like ${perl{foo}} (which is similar to the one used in the page you are copying from) and one like ${perl{foo}{argument}} for calling a subroutine and passing it an argument. Nowhere does it mention syntax like yours:
${perl{randomhosts}%:*}
I'm not sure where you have got that syntax from, but it seems likely that this is what is causing your first error.
In a comment, you say
I am stying to get first part of string before colon for each random array value for "interface" and part after colon for "helo_data"
It seems to me that Exim doesn't support this requirement. You would need to call the function twice to get the two pieces of information that you require. You might be able to do this in the Perl using something like state variables - but it would be far more complex than the code you currently have there.
Secondly, your Perl code has a syntax error, so even if Exim was able to call your code, it wouldn't work.
The code you're copying sets up #inet like this:
#inet = ("x.x.x.1", "x.x.x.2", "x.x.x.3", "x.x.x.4");
Your equivalent code is this:
#inet = (
"x.x.x.1:hostname1.domain.com",
"x.x.x.2:hostname2.domain.com,
x.x.x.3:hostname3.domain.com
);
I've reformatted it, to make the problems more obvious. You are missing a number of quote marks around the elements of the array. (Note: I see that while I have been writing this answer, you have fixed that.)
Update: Ok, here is some code to put into exim.pl that does what you want.
use feature qw[state];
sub randomhosts {
state $current;
my #inet = (
"x.x.x.1:hostname1.domain.com",
"x.x.x.2:hostname2.domain.com",
"x.x.x.3:hostname3.domain.com"
);
if ($_[0] eq 'generate') {
shift;
#{$current}{qw[ip host]} = split /:/, $inet[rand #inet];
}
return $current->{$_[0]};
}
It generates a new ip/host pair if its first argument is 'generate'. It will then return either the hostname or the ip address from the generated pair. I think you can probably call it from your Exim config file like this:
dkim_remote_smtp:
driver = smtp
interface = "${perl{randomhosts}{generate}{ip}}"
helo_data = "${perl{randomhosts}{host}}"
But I'm no expert in Exim, so that syntax might need tweaking.
First I would like to note I have not worked with exim so I cannot say what exactly you are trying to do and why you have done things exactly so.
In the link you posted, a method called 'randinet' is added to exim.pl and the interface line in exim.conf is replaced by
interface = "${perl{randinet}}"
You have implemented a 'randomhosts' method and replaced the interface line with
interface = "${perl{randomhosts}%:*}"
Now the parser complains about not finding the closing bracket. That is likely due to the symbols you felt free to add but the parser does not have the freedom to ignore.
I suggest you try
interface = "${perl{randomhosts}}"
I'm using the checkcode function in MATLAB to give me a struct of all error messages in a supplied filename along with their McCabe complexity and ID associated with that error. i.e;
info = checkcode(fileName, '-cyc','-id');
In MATLAB's preferences, there is a list of all possible errors, and they are broken down into categories. Such as "Aesthetics and Readability", "Syntax Errors", "Discouraged Function Usage" etc.
Is there a way to access these categories using the error ID gained from the above line of code?
I tossed around different ideas in my head for this question and was finally able to come up with a mostly elegant solution for how to handle this.
The Solution
The critical component of this solution is the undocumented -allmsg flag of checkcode (or mlint). If you supply this argument, then a full list of mlint IDs, severity codes, and descriptions are printed. More importantly, the categories are also printed in this list and all mlint IDs are listed underneath their respective mlint category.
The Execution
Now we can't simply call checkcode (or mlint) with only the -allmsg flag because that would be too easy. Instead, it requires an actual file to try to parse and check for errors. You can pass any valid m-file, but I have opted to pass the built-in sum.m because the actual file itself only contains help information (as it's real implementation is likely C++) and mlint is therefore able to parse it very rapidly with no warnings.
checkcode('sum.m', '-allmsg');
An excerpt of the output printed to the command window is:
INTER ========== Internal Message Fragments ==========
MSHHH 7 this is used for %#ok and should never be seen!
BAIL 7 done with run due to error
INTRN ========== Serious Internal Errors and Assertions ==========
NOLHS 3 Left side of an assignment is empty.
TMMSG 3 More than 50,000 Code Analyzer messages were generated, leading to some being deleted.
MXASET 4 Expression is too complex for code analysis to complete.
LIN2L 3 A source file line is too long for Code Analyzer.
QUIT 4 Earlier syntax errors confused Code Analyzer (or a possible Code Analyzer bug).
FILER ========== File Errors ==========
NOSPC 4 File <FILE> is too large or complex to analyze.
MBIG 4 File <FILE> is too big for Code Analyzer to handle.
NOFIL 4 File <FILE> cannot be opened for reading.
MDOTM 4 Filename <FILE> must be a valid MATLAB code file.
BDFIL 4 Filename <FILE> is not formed from a valid MATLAB identifier.
RDERR 4 Unable to read file <FILE>.
MCDIR 2 Class name <name> and #directory name do not agree: <FILE>.
MCFIL 2 Class name <name> and file name do not agree: <file>.
CFERR 1 Cannot open or read the Code Analyzer settings from file <FILE>. Using default settings instead.
...
MCLL 1 MCC does not allow C++ files to be read directly using LOADLIBRARY.
MCWBF 1 MCC requires that the first argument of WEBFIGURE not come from FIGURE(n).
MCWFL 1 MCC requires that the first argument of WEBFIGURE not come from FIGURE(n) (line <line #>).
NITS ========== Aesthetics and Readability ==========
DSPS 1 DISP(SPRINTF(...)) can usually be replaced by FPRINTF(...).
SEPEX 0 For better readability, use newline, semicolon, or comma before this statement.
NBRAK 0 Use of brackets [] is unnecessary. Use parentheses to group, if needed.
...
The first column is clearly the mlint ID, the second column is actually a severity number (0 = mostly harmless, 1 = warning, 2 = error, 4-7 = more serious internal issues), and the third column is the message that is displayed.
As you can see, all categories also have an identifier but no severity, and their message format is ===== Category Name =====.
So now we can just parse this information and create some data structure that allows us to easily look up the severity and category for a given mlint ID.
Again, though, it can't always be so easy. Unfortunately, checkcode (or mlint) simply prints this information out to the command window and doesn't assign it to any of our output variables. Because of this, it is necessary to use evalc (shudder) to capture the output and store it as a string. We can then easily parse this string to get the category and severity associated with each mlint ID.
An Example Parser
I have put all of the pieces I discussed previously together into a little function which will generate a struct where all of the fields are the mlint IDs. Within each field you will receive the following information:
warnings = mlintCatalog();
warnings.DWVRD
id: 'DWVRD'
severity: 2
message: 'WAVREAD has been removed. Use AUDIOREAD instead.'
category: 'Discouraged Function Usage'
category_id: 17
And here's the little function if you're interested.
function [warnings, categories] = mlintCatalog()
% Get a list of all categories, mlint IDs, and severity rankings
output = evalc('checkcode sum.m -allmsg');
% Break each line into it's components
lines = regexp(output, '\n', 'split').';
pattern = '^\s*(?<id>[^\s]*)\s*(?<severity>\d*)\s*(?<message>.*?\s*$)';
warnings = regexp(lines, pattern, 'names');
warnings = cat(1, warnings{:});
% Determine which ones are category names
isCategory = cellfun(#isempty, {warnings.severity});
categories = warnings(isCategory);
% Fix up the category names
pattern = '(^\s*=*\s*|\s*=*\s*$)';
messages = {categories.message};
categoryNames = cellfun(#(x)regexprep(x, pattern, ''), messages, 'uni', 0);
[categories.message] = categoryNames{:};
% Now pair each mlint ID with it's category
comp = bsxfun(#gt, 1:numel(warnings), find(isCategory).');
[category_id, ~] = find(diff(comp, [], 1) == -1);
category_id(end+1:numel(warnings)) = numel(categories);
% Assign a category field to each mlint ID
[warnings.category] = categoryNames{category_id};
category_id = num2cell(category_id);
[warnings.category_id] = category_id{:};
% Remove the categories from the warnings list
warnings = warnings(~isCategory);
% Convert warning severity to a number
severity = num2cell(str2double({warnings.severity}));
[warnings.severity] = severity{:};
% Save just the categories
categories = rmfield(categories, 'severity');
% Convert array of structs to a struct where the MLINT ID is the field
warnings = orderfields(cell2struct(num2cell(warnings), {warnings.id}));
end
Summary
This is a completely undocumented but fairly robust way of getting the category and severity associated with a given mlint ID. This functionality existed in 2010 and maybe even before that, so it should work with any version of MATLAB that you have to deal with. This approach is also a lot more flexible than simply noting what categories a given mlint ID is in because the category (and severity) will change from release to release as new functions are added and old functions are deprecated.
Thanks for asking this challenging question, and I hope that this answer provides a little help and insight!
Just to close this issue off. I've managed to extract the data from a few different places and piece it together. I now have an excel spreadsheet of all matlab's warnings and errors with columns for their corresponding ID codes, category, and severity (ie, if it is a warning or error). I can now read this file in, look up ID codes I get from using the 'checkcode' function and draw out any information required. This can now be used to create analysis tools to look at the quality of written scripts/classes etc.
If anyone would like a copy of this file then drop me a message and I'll be happy to provide it.
Darren.
I have a makefile with the following format. First I define what my outputs are;
EXEFILES = myexe1.exe myexe2.exe
Then I define what the dependencies are for those outputs;
myexe1.exe : myobj1.obj
myexe2.exe : myobj2.obj
Then I have some macros that define extra dependencies for linking;
DEP_myexe1 = lib1.lib lib2.lib
DEP_myexe2 = lib3.lib lib4.lib
Then I have the target for transforming .obj to .exe;
$(EXEFILES):
$(LINK) -OUT:"Exe\$#" -ADDOBJ:"Obj\$<" -IMPLIB:$($($(DEP_$*)):%=Lib\\%)
What I want to happen is (example for myexe1.exe)
DEP_$* -> DEP_myexe1
$(DEP_myexe1) -> lib1.lib lib2.lib
$(lib1.lib lib2.lib:%=Lib\\%) -> Lib\lib1.lib Lib\lib2.lib
Unfortunately this is not working. When I run make --just-print, the -IMPLIB: arguments are empty. However, if I run $(warning DEP_$*) I get
DEP_myexe1
And when I run $(warning $(DEP_myexe1)) I get
lib1.lib lib2.lib
So for some reason, make does not like the combination of $(DEP_$*). Perhaps it cannot resolve macro names dynamically like this. What can I do to get this to work? Is there an alternative?
Where does $(warning DEP_$*) give you DEP_myexe1 as output exactly? Because given your makefile above it shouldn't.
$* is the stem of the target pattern that matched. In your case, because you have explicit target names, you have no patten match and so no stem and so $* is always empty.
Additionally, you are attempting a few too many expansions. You are expanding $* to get myexe1 directly (assuming for the moment that variable works the way you intended). You then prefix that with DEP_ and used $(DEP_$*) to get the lib1.lib lib2.lib. You then expand that result $($(DEP_$*)) and then expand that (empty) result again (to do your substitution) $($($(DEP_$*)):%=Lib\\%).
You want to either use $(#:.exe=) instead of $* in your rule body or use %.exe as your target and then use $* to get myexe1/myexe2.
You then want to drop two levels of expansion from $($($(DEP_$*)):%=Lib\\%) and use $(DEP_$*:%=Lib\\%) instead.
So (assuming you use the pattern rule) you end up with:
%.exe:
$(LINK) -OUT:"Exe\$#" -ADDOBJ:"Obj\$<" -IMPLIB:$(DEP_$*:%=Lib\\%)
I managed to get it working without needing to resolve macros in the way described above. I modified the linking dependencies like this;
myexe1.exe : myobj1.obj lib1.lib lib2.lib
myexe2.exe : myobj2.obj lib3.lib lib4.lib
Then I need to filter these files by extension in the target recipe;
$(EXEFILES):
$(LINK) -OUT:"$(EXE_PATH)\$#" -ADDOBJ:$(patsubst %, Obj\\%, $(filter %.obj, $^)) -IMPLIB:$(patsubst %, Lib\\%, $(filter %.lib, $^))
The $(pathsubst ...) is used to prepend the path that the relevant files are in.
In the case of myexe1.exe, the link command expands to;
slink -OUT:"Exe\myexe1.exe" -ADDOBJ: Obj\myexe1.obj -IMPLIB: Lib\lib1.lib Lib\lib2.lib
Out of interest's sake, I would still like to know if it is possible to resolve macro names like in the question.