2013-02-28 18 views
5

in Matlab, si può scrivere:Come "trasmettere" una funzione a due argomenti in una funzione a un argomento?

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

Se ho una funzione che si aspetta una funzione one-argomento, posso fare quanto sopra. Come posso farlo in c/C++?

Ho una funzione di libreria (dalla libreria CGAL) che prevede come argomento una funzione che ha esso stesso un solo argomento. Idealmente, ho una classe (SphericalHarmonics) e vorrei avere una funzione membro che accetta l'argomento. Così ho:

FT SphericalHarmonics::distFunction(Point_3 p) 

(si noti che FT è un tipo simile a double), ma naturalmente quando provo

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

this viene trattato come argomento, la mia funzione distFunction è un due argomento, e viene generato un errore.

noti che questo può essere risolto con variabili globali, cioè

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

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

Tuttavia, questo è davvero non ideale. Mi piacerebbe un modo per farlo senza variabili globali, dato che sarebbe molto meglio avere una funzione di classe che si integri facilmente con la libreria CGAL.

Grazie in anticipo!

[AGGIORNATO]

@ Andy-Prowl: ho provato le soluzioni di std::bind e lambda, ma sembrano ancora essere in esecuzione in errori per quanto riguarda il numero di argomenti.

Quando nel main, io uso il codice:

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.)); 

ottengo gli errori:

~/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)’ 

e

~/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>)}’ 

e

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

[AGGIORNATO]

Ora è chiaro che ho bisogno di una funzione che può essere convertito in un puntatore alla funzione (cioè surface richiesto un argomento puntatore di funzione). Questo esclude l'opzione std::bind. Inoltre, sembra che un lambda non possa essere convertito in un puntatore a funzione se cattura le variabili (capture-less vs. capturing lambdas). Quindi penso che la risposta di Andy-Prowl qui sotto sia in generale la risposta corretta a questa domanda, anche se dovrò trovare un work-around diverso.

+3

(non ho davvero leggere la domanda) si legano o un lambda? –

+0

I tag sono fuorvianti, dovrebbero contenere matlab/lambda – Dmitry

+0

Alcune di queste soluzioni funzionano davvero? Non è possibile utilizzare il bind per sostituire il puntatore alla funzione? –

risposta

5

OPZIONE 1:

Nel caso la vostra funzione di membro non per lavorare in modo implicito in un'istanza della classe (e quindi non ha bisogno di ricevere un this puntatore), si può rendere static:

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

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

Ora, la funzione avrà effettivamente un unico argomento:

surface(SphericalHarmonics::distFunction); 

OPZIONE 2:

In caso contrario, è possibile utilizzare std::bind() per accattivarsi la funzione di membro distFunction e fissare il suo primo, argomento implicito (se si non si lavora con un compilatore C++ 11, è possibile utilizzare l'equivalente boost::bind() dalla libreria Boost.Bind):

#include <functional> 

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

OPZIONE 3:

In alternativa, in C++ 11, un lambda potrebbe fare il lavoro:

SphericalHarmonics *sh = new SphericalHarmonics(1); 
auto fxn = [=] (double d) { return sh->distFunction(d); } 
+0

Grazie! Sono sicuro che sia corretto quindi l'ho già segnato. L'opzione 1 non è un'opzione (voglio lavorare su istanze di classi specifiche). Quando provo l'opzione 2 o 3 ottengo l'errore 'error: 'fxn' non indica un tipo 'e' error: 'fxn' non è stato dichiarato in questo ambito. Ho provato sia 'std :: bind' che' boost :: bind' (incluso '' e '') e ottengo lo stesso errore. Usando gcc versione 4.6.3 su ubuntu 12.04 – OwenM

+0

@OwenM: Stai usando un compilatore C++ 11 con l'opzione '-std = C++ 0x' o' -std = C++ 11'? La parola chiave 'auto' richiede C++ 11. Ti suggerisco anche di effettuare l'aggiornamento a GCC 4.7.2, la versione che stai utilizzando è piuttosto vecchia. C'è anche [questo sito] (http://www.liveworkspace.org) dove puoi compilare il tuo codice con diversi compilatori. –

+0

Sì, gcc 4.7.2 con -std = C++ 11 risolto questo problema! – OwenM

5

Usa std::bind o boost::bind:

#include <functional> 

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

Vuoi dire sh invece di questo? – Slava

+0

@Slava Sì, grazie. – Drax

1

Nel caso specifico di trattare con su modelli Surface_3 classe di CGAL. Probabilmente stai usando qualcosa di simile (dal loro esempio) per definire Surface_3 Tipo:

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; 

Questo è fuorviante perché fa sembrare come classi Isosurface di CGAL possono trattare solo con puntatori a funzione (al contrario di std::function eccetera.). Ma il problema è solo che abbiamo appena definito in questo modo. Basta definire Surface_3 utilizzare std::function<FT (Point_3)> come argomento di modello e il std::bind e lambda da Andy Prowl's answer funzionano bene:

... 
typedef std::function<FT (Point_3)> Function; 
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3; 
Problemi correlati