5

In matlab, one can write:

S = @(x,y) x^2+y^2-1
G = @(x) S(x,1);

If I have a function expecting a one-argument function, I can do the above. How can I do this in c/c++?

I have a library function (from the CGAL library) that expects as an argument a function that itself has only one argument. Ideally, I have a class (SphericalHarmonics) and I would like to have a member function which takes the one argument. So I have:

FT SphericalHarmonics::distFunction(Point_3 p)

(note that FT is a type similar to double) but of course when I try

SphericalHarmonics *sh = new SphericalHarmonics(1);
Surface_3 surface(sh->distFunction, Sphere(ORIGIN,2.));

this is also treated as an argument, my distFunction function is a two-argument function, and an error is thrown.

Note that this can be solved with global variables, i.e.

SphericalHarmonics *sh;
FT dist_function(Point_3 p) {
    return sh->distFunction(p);
}

main() {
    sh = new SphericalHarmonics(1);
    Surface_3 surface(dist_function);
}

However, this is really non-ideal. I'd like a way to do this without global variables, as it would be far better to be able to have a class function that easily integrates with the CGAL library.

Thanks in advance!

[UPDATED]

@Andy-Prowl: I have tried your std::bind and lambda solutions, but still seem to be running into errors with regards to the number of arguments.

When, in main, I use the code:

SphericalHarmonics *sh = new SphericalHarmonics(cInit, numL, symm);
auto fxn = std::bind(&SphericalHarmonics::distFunction, sh, std::placeholders::_1);
Surface_3 surface(fxn, Sphere_3(ORIGIN,2.));

I get the errors:

~/lib/basisfunctions/SphericalHarmonics2/mesh_an_implicit_function.cpp:62:48: 
error: no matching function for call to     
‘CGAL::Implicit_surface_3<CGAL::Robust_circumcenter_traits_3<CGAL::Epick>, 
double (*)
(CGAL::Point_3<CGAL::Epick>)>::Implicit_surface_3(std::_Bind<std::_Mem_fn
<double (SphericalHarmonics::*)(CGAL::Point_3<CGAL::Epick>)>
(SphericalHarmonics*, std::_Placeholder<1>)>&, Sphere_3)’

and

~/CGAL-4.1/include/CGAL/Implicit_surface_3.h:50:5: note:   no known conversion 
for argument 1 from ‘std::_Bind<std::_Mem_fn<double (SphericalHarmonics::*)
(CGAL::Point_3<CGAL::Epick>)>(SphericalHarmonics*, std::_Placeholder<1>)>’ to 
‘CGAL::Implicit_surface_3<CGAL::Robust_circumcenter_traits_3<CGAL::Epick>, 
double (*)(CGAL::Point_3<CGAL::Epick>)>::Function 
{aka double (*)(CGAL::Point_3<CGAL::Epick>)}’

and

~/CGAL-4.1/include/CGAL/Implicit_surface_3.h:34:9: note:   
candidate expects 1 argument, 2 provided

[UPDATED]

It is now clear to me that I need a function which can be converted to a function pointer (i.e. surface required a function pointer argument). This rules out the std::bind option. Moreover, it appears that a lambda cannot be converted to a function pointer if it captures variables (capture-less vs. capturing lambdas). So I think Andy-Prowl's answer below is in general the correct answer to this question, although I'll need to find a different work-around.

Community
  • 1
  • 1
OwenM
  • 53
  • 4

3 Answers3

5

Use std::bind or boost::bind :

#include <functional>   

SphericalHarmonics *sh = new SphericalHarmonics(1);
surface(std::bind(&SphericalHarmonics::distFunction, sh, _1));
Drax
  • 12,682
  • 7
  • 45
  • 85
5

OPTION 1:

In case your member function does not to implicitly work on an instance of your class (and therefore does not need to receive a this pointer), you can make it static:

class SphericalHarmonics
{
    ...
    static double distFunction(Point p);
    ...
};

double SphericalHarmonics::distFunction(Point p)
{
    ...
}

Now, your function will effectively have a single argument:

surface(SphericalHarmonics::distFunction);

OPTION 2:

Otherwise, you may use std::bind() to curry the member function distFunction and fix its first, implicit argument (if you are not working with a C++11 compiler, you can use the equivalent boost::bind() from the Boost.Bind library):

#include <functional>

SphericalHarmonics *sh = new SphericalHarmonics(1);
auto fxn = std::bind(&SphericalHarmonics::distFunction, sh, _1);
surface(fxn);

OPTION 3:

Alternatively, in C++11, a lambda could do the job:

SphericalHarmonics *sh = new SphericalHarmonics(1);
auto fxn = [=] (double d) { return sh->distFunction(d); } 
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Thanks! I'm sure this is correct so I've already check-marked it. Option 1 is not an option (I want to work on specific class instances). When I try either Option 2 or 3 I get the errors `error: 'fxn' does not name a type` and `error: 'fxn' was not declared in this scope. I've tried both `std::bind` and `boost::bind` (includind `` and ``) and I get the same error. Using gcc version 4.6.3 on ubuntu 12.04 – OwenM Feb 28 '13 at 01:10
  • @OwenM: Are you using a C++11 compiler with the `-std=c++0x` or `-std=c++11` command-line option? The `auto` keyword requires C++11. I also suggest you to upgrade to GCC 4.7.2, the version you are using is quite old. There is also [this website](http://www.liveworkspace.org) where you can compile your code with different compilers. – Andy Prowl Feb 28 '13 at 01:17
  • Yep, gcc 4.7.2 with -std=c++11 fixed this issue! – OwenM Feb 28 '13 at 17:00
  • @OwenM: OK, that's good to hear, but why did you un-accept this answer? – Andy Prowl Feb 28 '13 at 17:09
  • Sorry, see above. I'm still having difficulties getting compilation, and it seems to be a real issue (i.e. not a wrong-compiler type of issue). Should I have provided my issues in a direct comment to you, as opposed to editing my original post? I'm still figuring out the protocols here... – OwenM Feb 28 '13 at 17:11
  • @OwenM: OK, but that's not the way of notifying this. I don't receive a notification if you address me that way in the question's text, so I totally missed it. – Andy Prowl Feb 28 '13 at 17:12
  • @OwenM: Also, the fact that you have *further* issues with a *different* code is no reason for un-accepting an answer. Rather, you should ask another one. I'm sure you're using `bind` the wrong way, but I need to see more code to help. Please, re-accept this answer, make a new question out of the new code, where you show all the necessary definitions, and I'll try to help. – Andy Prowl Feb 28 '13 at 17:17
  • Understood, thanks (with regards to the notifications). For my original code your answer doesn't work. I'll be happy to remove my new examples (which simply replaces my class function with a simple stand-alone function) and show the error messages with the code examples you provided, if you'd prefer. – OwenM Feb 28 '13 at 17:19
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/25306/discussion-between-owenm-and-andy-prowl) – OwenM Feb 28 '13 at 17:33
1

In the specific case of dealing with CGAL's templated Surface_3 class. You're probably using something like this (from their example) to define Surface_3 type:

typedef CGAL::Surface_mesh_default_triangulation_3 Tr;
// c2t3
typedef CGAL::Complex_2_in_triangulation_3<Tr> C2t3;
typedef Tr::Geom_traits GT;
typedef GT::Sphere_3 Sphere_3;
typedef GT::Point_3 Point_3;
typedef GT::FT FT;
typedef FT (*Function)(Point_3);
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3;

This is misleading because it makes it seem like CGAL's isosurface classes can only deal with function pointers (as opposed to std::function etc.). But the problem is only that we've just defined it that way. Simply define Surface_3 to use std::function<FT (Point_3)> as its template argument and the std::bind and lambdas from Andy Prowl's answer will work just fine:

...
typedef std::function<FT (Point_3)> Function;
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3;
Community
  • 1
  • 1
Alec Jacobson
  • 6,032
  • 5
  • 51
  • 88