0

I have a code similar to the minimal example below, where the function g is provided by a library and expects double (*)(double) as an argument. I cannot change this. The example code does not compile because the member has signature double (*C::)(double) as explained in e.g. this post, with a number of possible solutions.

#include <iostream>

double g(double (*f)(double x)) { return f(0); };

class C
{   
   public:
      C(double b) { a = b; };
      double f2() { return g(&f1); };
   private:
      double a;
      double f1(double x) { return x + a; };
};

int main()
{
   C c (1);
   std::cout << c.f2() << std::endl;
   return 0;
}

I wonder what the best way to implement this is given that I don't want to point to C::f1 outside the class but within another member function. As far as I understand, the member function C::f1 is not static since it is only fully known after an instance of the class is initialised. Since speed is also a concern: would this be a problem with any of the possible solutions proposed elsewhere for similar versions of this issue?

Coeus
  • 43
  • 6
  • Actually, the value ``C::a`` will be calculated from another function based on the what the user instantiates the class with; I just simplified it for the example; in reality it's a non-trivial computation. – Coeus Feb 03 '22 at 07:59
  • 1
    Indeed, I fixed it now – Coeus Feb 03 '22 at 08:03
  • 1
    `&f1` is [not legal](https://godbolt.org/z/vPaYjYbb4) to begin with. – n. m. could be an AI Feb 03 '22 at 08:10
  • 1
    `g` is written in such a way that it cannot be passed a well-behaved reliable function `f`. The only way for `f` to return something non-trivial (i.e. not the same number every time) is to access global data. `g` **must** be changed, otherwise the library is not fit for use in a serious software project. – n. m. could be an AI Feb 03 '22 at 08:26
  • Thinking more about this (also from the comments of the others) this seems to be the actual issue; I should make a pull request for the library that provides ``g`` to make it more general. – Coeus Feb 03 '22 at 09:04
  • @Coeus Is it an open library? Can you link to it? Other proposals could benefit from seeing the actual library to get the full picture. – Ted Lyngmo Feb 03 '22 at 17:07

2 Answers2

2

Make your f1 function static:

  static double f1(double x) { return x + a; };

This gives it the same signature as what your g function expects. Since a static function does not have a hidden this pointer, you will have to find some other way to get the value of a into it.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • I tried that, but then ``C::a`` is not static and the compiler complains. I only know the value of ``a`` once I initialise the class, unfortunately, so I cannot set ``double C::a = 1`` outside. – Coeus Feb 03 '22 at 07:49
  • @Coeus Since you can't change `g` and it has no idea of what instance of `C` you want to call `f1` on, you need to rethink this. You could make `a` `static` too. [example](https://godbolt.org/z/zfdz3qnvb) – Ted Lyngmo Feb 03 '22 at 07:55
  • 1
    @Coeus Thus, the note about "some other way to get the value of `a` into it". You're effectively trying to shove a C++ non-static member function square peg into a C function pointer round hole. The only way to do that *and* retain the C function pointer round hole is to make the C++ peg round as well, and that ain't happening with a non-static member function. It *can* happen with a static member (as Greg describes). An alternative is to change `g`, but I suspect that isn't on your list of options. – WhozCraig Feb 03 '22 at 07:56
  • Yes, that makes sense. I could ask the author of the library to change ``g`` or give more options for possible signatures. It's annoying because everything is in principle known once the class is instantiated, so it feels like there should be a way to run ``g`` inside the class. I guess I could write another function that takes the instance of the class and runs ``g`` externally. – Coeus Feb 03 '22 at 08:09
  • I understand the oddity, but it really isn't odd at all. As a non-static member you cannot run `f1` without an instance of `C`. There's no "there" there without the instance. `g`, as shown in the question, provides no facility to allow that. Making `f1` static eliminates the requirement of having an instance via the penalty of... not having an instance (and thus no non-static member `a` access, because, again, no "there" there). – WhozCraig Feb 03 '22 at 09:14
0

Okay, based on the comments and ideas from the post mentioned before, I will suggest to change g as follows and add a wrapper function f2_wrapper.

#include <iostream>

double g(double (*f)(double x, void *context), void *context)
{
   return f(0, context);
};

class C
{   
    public:
        C(double b) { a = b; };
        double f1(double x) { return x + a; };
        double f2();
    private:
        double a;
};

double f2_wrapper(double x, void* context)
{
    C *c = (C*) context;
    return c->f1(x);
}

double C::f2() { return g(&f2_wrapper, this); }

int main()
{
    C c (1);
    std::cout << c.f2() << std::endl;
    return 0;
}
Coeus
  • 43
  • 6
  • It is required that the declaration of the function g cannot be changed since it is part of an external librarry. – 20knots Feb 03 '22 at 09:53
  • @20knots As stated, I cannot change ``g`` myself. However, this is a small external library project, so think I will ask them to make the change. But you're right; this goes against the spirit of the question so other solution proposals are still welcome! – Coeus Feb 03 '22 at 10:51