3

I am currently writing my first XS-module (just a wrapper around the C math-library) with ok-ish success. The biggest issue is the documentation that is quite hard to understand and/or is incomplete.

I have successfully written a constructor in XS and implemented some functions out of the library as method calls. That works fine.

Now I want to implement a procedural interface, too. For this reason I need to know if its a method call or not. If its a method call the number to compute with the function is stored in the instance, if its a procedural call to a function its a number given as the first argument. This is the current code for the cosine-function:

double
cos(...)
    CODE:
        SV *arg = newSVsv(ST(0));
        if (sv_isobject(arg)) {
            HV *self_hv = MUTABLE_HV(SvRV(arg));
            SV **callback_ptr = hv_fetchs(self_hv, "Number", 0);
            SV *zahl = *callback_ptr;
        }
        else {
            SV *zahl = newSVnv(arg);
        }

        double x = SvNV(zahl);
        RETVAL = cos(x);
    OUTPUT:
        RETVAL
user2875983
  • 303
  • 2
  • 6
  • You seem to have forgotten to ask a question. You didn't even hint at a problem. What do you want? – ikegami Oct 23 '14 at 16:40
  • Note: You use `ST(0)` without checking how many arguments you got. – ikegami Oct 23 '14 at 16:41
  • My problem is: when i call it as a method without the if and the whole else block it works. So I guess my problem is somewhere in the if-condition. Note: the code doesnt even compile, it throws a error on the line with the assignment of the double (first time used but not declared, so i assumed the if-condition must be the problem here – user2875983 Oct 23 '14 at 20:33
  • First, you don't mention there is an error. Now, you don't mention what it is? We can't read your mind (or your screen)! – ikegami Oct 24 '14 at 16:14

2 Answers2

5

Generally speaking, writing subs which are intended to be called as either methods or functions is a bad idea. There are one or two well known modules that do this (CGI.pm springs to mind), but for the most part this is confusing for end users, and unnecessarily complicates your own code. Nobody will thank you for it. Pick one style and stick with it.

Let's assume you've chosen to stick with OO programming. Then, once your module is working and tested, you can write a second module, offering exportable functions instead of an OO interface. The second module can probably be written in plain old Perl, just acting as a wrapper around your OO module.

As an example, using pure Perl (and not XS) for readability:

use v5.14;

package MyStuff::Maths::OO {
   use Class::Tiny qw(number);
   sub cosine {
      my $self   = shift;
      my $number = $self->number;
      ...;
      return $cosine;
   }
}

package MyStuff::Maths::Functional {
   use Exporter::Shiny qw(cosine);
   sub cosine {
      my $obj = ref($_[0]) ? $_[0] : MyStuff::Maths::OO->new(number => $_[0]);
      return $obj->cosine;
   }
}

Now end users can choose to use your OO interface like this:

use v5.14;
use MyStuff::Maths::OO;

my $obj = MyStuff::Maths::OO->new(number => 0.5);
say $obj->cosine;

Or use the functional interface:

use v5.14;
use MyStuff::Maths::Functional -all;

say cosine(0.5);
tobyink
  • 13,478
  • 1
  • 23
  • 35
  • I get your point. In general I would also do it this way. But I started to work on this particular thing to learn about XS. So it was not the point to write a wrapper in normal Perl. – user2875983 Oct 23 '14 at 13:06
  • @user2875983, Actually, it is more so than you think. If you want to learn to use XS, then learning what should be done on the Perl side and what shouldn't is a useful lesson. – ikegami Oct 23 '14 at 16:38
  • @user2875983, there's nothing to stop you from also writing the wrapper in XS if you so desire. There would be little advantage in doing so, apart from the speed gain. My main point is that separating these two interfaces into two modules will save your sanity, as well as the sanity of your end users. This is the same whether you are writing XS or pure Perl. – tobyink Oct 23 '14 at 21:22
0

In my case it was a simple problem, and I simply didn't see it. The declaration of the SV *zahl inside the if-else-statement was the problem. Predeclaration before the if was the key to the solution.

But I agree with tobyinks solution for modules that get used by other people or published somewhere.

user2875983
  • 303
  • 2
  • 6