C99: Pointer naar functie met argumenten

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 27-09 22:01
Sterk versimpelt voorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void add(int *a, int b) {
  (*a) += b;
}

void manipulate_with_function(int *a, void (*fn)(int *a)) {
  (*fn)(*a);
}

int main(int argc, char **argv) {
  int target_number = 2;
  int number_to_add = 3;
  void (*manipulation_function)(int *) = add(..., number_to_add)
  manipulate_with_function(&i, add);
  printf("%i\n", i); // => 5
}


Ik wil een pointer naar "add" waarvan het 2e argument al vooraf is ingevuld met data uit een variable. Omdat ik niet helemaal weet hoe dit in C zou werken, hierbij een werkend voorbeeld in Javascript:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function add(pointer, b) {
  pointer.number += b;
}

function manipulateWithFunction(pointer, fn) {
  fn(pointer);
}

function main(pointer) {
  var targetNumberPointer = { number: 2 };
  var numberToAdd = 3;
  var manipulationFunction = function(pointer) { add(pointer, numberToAdd) }
  manipulateWithFunction(targetNumberPointer, manipulationFunction);
  console.log(targetNumberPointer.number);
}

main();


Ik heb gezocht, maar ik kom voornamelijk op voorbeelden van pointers naar functions; ik wil soort-van "pre-bound variablen" hebben, maar ik weet niet precies hoe ik erop moet zoeken. Is wat ik wil mogelijk in C? Zo ja, hoe?

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
add is geen function die voldoet aan de signature van jouw function, en C99 heeft niet zoiets als lambda functions waar je inline een nieuwe functie definieert ( Dat is immers ook wat je in je javascript voorbeeld doet ).

Je zult dus een losse adapter functie moeten definiëren die wel voldoet aan jouw signature.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

Wat jij wil kan inderdaad niet.
Eventueel kun je zoiets doen?:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct MyFunctStruct {
  void (*functionPtr)(int *,int);
  int argument;
}

void add(int *a, int b) {
  (*a) += b;
}

void manipulate_with_function(int *a, struct MyFunctStruct* aMyFunctStruct) {
 aMyFunctStruct->functionPtr(a, aMyFunctStruct->argument);
}

int main(int argc, char **argv) {
  int target_number = 2;
  int number_to_add = 3;
  struct MyFunctStruct myFunctStruct = {.functionPtr = &add, .argument = number_to_add };
  manipulate_with_function(&target_number, myFunctStruct );
  printf("%i\n", i); // => 5
}


Alleen of dit nou veel handiger is dan de functie direct met de goede argumenten aanroepen....

[ Voor 8% gewijzigd door EddoH op 22-03-2016 09:36 ]


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 27-09 22:01
Bedankt voor de tips :) Ik ga even mijn keuzes heroverwegen

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
Dit vraagt om een een beetje preprocessor magic!

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Ik vraag me sowieso af waarom dit handig/nuttig is, tov het direct aanroepen van de functie.

Acties:
  • 0 Henk 'm!

  • jeroen3
  • Registratie: Mei 2010
  • Laatst online: 10:38
Van het tweede argument ook een pointer maken, en deze maak je NULL om een standaard waarde te gebruiken. Vrij standaard in C volgens mij.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
Ik heb zoiets eerlijk gezegd nog nooit gezien/gedaan, bovendien vind ik het niet echt mooi om een pointer te gaan gebruiken waar je eigenlijk geen pointer wilt.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

farlane schreef op dinsdag 22 maart 2016 @ 21:07:
Ik heb zoiets eerlijk gezegd nog nooit gezien/gedaan, bovendien vind ik het niet echt mooi om een pointer te gaan gebruiken waar je eigenlijk geen pointer wilt.
Dit. Als het nu ging om een buffer zelf alloceren/laten alloceren ging dan is de optie voor NULL wel een goede, echter bij 'normale' argumenten zou ik dat niet zomaar doen.
Daarnaast gaat het probleem van de TS volgens mij niet echt alleen over default arguments.

Ben eigenlijk wel benieuwd naar de concrete use-case en gekozen oplossing van de TS. Care to shed a light, Gamebuster? :)

Acties:
  • 0 Henk 'm!

  • Particlebox
  • Registratie: September 2014
  • Laatst online: 12-12-2023
Waarom zou je dit überhaupt willen doen? Tenzij je spaghetti code wilt maken, dan kan dat zo :+

Ik zou het dan eerder oplossen door meteen op te tellen met één functie. Wil jij twee waarden die voor verschillende functies gebruikt moeten worden, waarom niet een class die deze objecten bewaard en controleert? (alhoewel ik nog steeds niet snap waarom je dit zo wilt)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
   class A {
    private:
      int m_a; //POD's als pointer is onnodig
      int m_b;
     public:
      A(int a, int b) : m_a(a), m_b(b) { 
      }
      
      inline int add() { return m_a + m_b; }
      inline int sub() { return m_a - m_b; }
      inline int mul() { return m_a * m_b; }
   }

Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

Actinide2k9 schreef op woensdag 23 maart 2016 @ 09:24:
Waarom zou je dit überhaupt willen doen? Tenzij je spaghetti code wilt maken, dan kan dat zo :+

Ik zou het dan eerder oplossen door meteen op te tellen met één functie. Wil jij twee waarden die voor verschillende functies gebruikt moeten worden, waarom niet een class die deze objecten bewaard en controleert? (alhoewel ik nog steeds niet snap waarom je dit zo wilt)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
   class A {
    private:
      int m_a; //POD's als pointer is onnodig
      int m_b;
     public:
      A(int a, int b) : m_a(a), m_b(b) { 
      }
      
      inline int add() { return m_a + m_b; }
      inline int sub() { return m_a - m_b; }
      inline int mul() { return m_a * m_b; }
   }
Het gaat over C, niet C++ ;)
Bij C++ zou je nog iets leuks met operator () overloading kunnen doen.

[ Voor 4% gewijzigd door EddoH op 23-03-2016 09:31 ]


Acties:
  • 0 Henk 'm!

  • Particlebox
  • Registratie: September 2014
  • Laatst online: 12-12-2023
EddoH schreef op woensdag 23 maart 2016 @ 09:29:
[...]


Het gaat over C, niet C++ ;)
Bij C++ zou je nog iets leuks met operator () overloading kunnen doen.
Ahh, ff een derpje heb niet goed gekeken. Toch nog even een bakje koffie opslurpen dan maar. :+

Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 27-09 22:01
De use-case is als volgt:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// begin some code i dont want to touch
typedef {
  ...
} Thingy;

typedef {
  Thingy thingy;
  int a;
} ThingyFoo;

typedef {
  int b;
  int c;
  ThingyFoo * thingy_foos;
} ThingyFooCollection;

iterate_thingies(ThingyFooCollection * collection, void (*fn)(Thingy * thingy)) {
  // call fn for every thingy_foo's thingy
}

do_something_for_thingy(Thingy * thingy, float foo) {
  // doo something with thingy & foo
}
// end of code i dont want to touch

// begin of my code
do_something_for_all_thingies(ThingyFooCollection * collection, float foo) {
  // call do_something_for_thingy for every thingy & foo in collection
  iterate_thingies(collection, ???)
}

do_something_for_all_thingies(my_collection, 1.1f);


Wellicht dat dit de use-case wat duidelijker maakt :)

Als je echt wil kijken wat er speelt, is hier de daadwerkelijke code:
https://github.com/tobyhi...ob/master/world.c#L27-L37

Ik wil world_update aanpassen zodat-ie world_iterate_units gebruikt. Ander commentaar is uiteraard ook welkom; ik ben enkel aan het spelen met C voor de fun :)

[ Voor 47% gewijzigd door Gamebuster op 23-03-2016 10:33 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
Je callback krijgt geen context/parameters mee vanuit je iterate. Je zou iets kunnen doen met varargs misschien?

[edit]
Generieke code schrijven in C is best lastig. In dat geval is C++ toch handiger.

[ Voor 29% gewijzigd door farlane op 23-03-2016 10:58 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • narotic
  • Registratie: Maart 2002
  • Laatst online: 02-11-2021
De functionaliteit die je wilt worden closures genoemd. Dat maakt het zoeken misschien wat makkelijker, maar C ondersteunt voor zover ik weet sowieso geen closures.

- = Step Into The Pit | Industrial Strength = -


Acties:
  • +1 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 11-10 19:15
Eigenlijk partial function application (std::bind in C++, bijvoorbeeld). Closures zijn maar één manier om dat te implementeren.

Acties:
  • 0 Henk 'm!

  • Sir Isaac
  • Registratie: September 2002
  • Laatst online: 21-05 20:45
Dit soort technieken wordt veel gebruikt een de numerieke wiskunde. Voorbeeld een functie die een andere functie integreert: I(f, a, b), waarbij f de functie is die geïntegreerd wordt en a en b de grenzen van het integratie domein. Ik denk daarom dat je in de Numerical Recipees veel voorbeelden moet kunnen vinden. De oorspronkelijke recipees waren in Fortran, maar tegenwoordig zijn ze ook naar C geport.

[ Voor 6% gewijzigd door Sir Isaac op 27-03-2016 12:49 ]


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Numerical Recipes in C is een boek van onschatbare waarde, voornamelijk in hoe je niet in C moet programmeren. Stikvol Undefined Behavior, waar vroegere compilers je mee weg lieten komen. Array indices bij [1] laten beginnen door de pointer vòòr de array te laten wijzen? (zodat [0] niet bestaat). Een moderne compiler ziet dat en concludeert "Unreachable code, can be optimized out".

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 27-09 22:01
Dit is het geworden: (voorbeeld is pseudo-code, niet getest). Bedankt allen voor de hulp :)

Ik gebruik nu 1 extra argument wat een pointer naar void is. Zo kan ik er alles aan meegeven :)

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// begin some code i dont want to touch
typedef {
  ...
} Thingy;

typedef {
  Thingy thingy;
  int a;
} ThingyFoo;

typedef {
  int b;
  int c;
  ThingyFoo * thingy_foos;
} ThingyFooCollection;

void iterate_thingies(ThingyFooCollection * collection, void * arg, void (*fn)(Thingy * thingy, void * arg)) {
  // call fn for every thingy_foo's thingy
}

void do_something_for_thingy(Thingy * thingy, float foo) {
  // doo something with thingy & foo
}
// end of code i dont want to touch

// begin of my code
void iterate_thingies_thingy(Thingy * thingy, void * foo) {
  do_something_for_thingy(thingy, *(*float)foo);
}


do_something_for_all_thingies(ThingyFooCollection * collection, float foo) {
  iterate_thingies(collection, &foo, iterate_thingies_thingy)
}

do_something_for_all_thingies(my_collection, 1.1f);

[ Voor 17% gewijzigd door Gamebuster op 04-04-2016 22:18 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 11-10 14:49
Gamebuster schreef op maandag 04 april 2016 @ 22:16:
Ik gebruik nu 1 extra argument wat een pointer naar void is. Zo kan ik er alles aan meegeven :)
Das misschien wel de meest gangbare optie, helaas is wel je type safety weg op die manier.

code:
1
do_something_for_thingy(thingy, *(*float)foo);

?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 27-09 22:01
farlane schreef op woensdag 06 april 2016 @ 09:08:
[...]

Das misschien wel de meest gangbare optie, helaas is wel je type safety weg op die manier.

code:
1
do_something_for_thingy(thingy, *(*float)foo);

?
Ik ben me daarvan bewust. In dit geval vind ik het acceptabel

Let op: Mijn post bevat meningen, aannames of onwaarheden

Pagina: 1