NOGDUS $1670.00 has been donated to NOGDUS!
August 22, 2017, 05:29:42 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: C++ - Implementing A Custom ForEach  (Read 5218 times)
0 Members and 2 Guests are viewing this topic.
Richard Marks
Administrator
Offline Offline

Respect: 3425
« on: March 25, 2010, 06:26:31 AM »

The following code can be useful for a zillion or more reasons.
I figured I would share this, since it is something that I found myself needing, so maybe you need it too. Cool

First, a little background.

A ForEach in the context that I am using it is a function that iterates over all the elements of an array and calls another user-specified function to manipulate each element of the array.

The example below illustrates the concept in a fully-compilable and working demonstration.
The example is trivial in that all it does is spit out the contents of each array of data in a specific manner.
The output of the example could be achieved in a much shorter and simpler code, however the focus is on illustrating how one can implement the ForEach code design, and this I believe it does.

My implementation revolves around a simple function pointer and arrays of void pointers.
The example makes use of a C++ template data structure so that its easily reused for many data types.

How it works:

First we create an array of data.
Then we create a DataBlock template object to hold our array and its size (number of elements)
Then we simply write a function that we will call once for every element in our array.
Finally we use the ForEach member function of the DataBlock template to do the work.

So, 1 line of code results in X+1 function calls where X is the number of elements in the array.
You should be seeing the power behind this very simple design.

And now, finally, here is the full source for the example.

My template data structure that implements the ForEach design:
Code:
// DataBlock.h
#ifndef DATABLOCK_H
#define DATABLOCK_H
template <typename T> struct DataBlock
{
T* data;
int size;
DataBlock(T* source, int count) { this->data = source; this->size = count; }

void ForEach(void(*function)(void**))
{
if (!function)
{
return;
}

for (int i = 0; i < size; i++)
{
void* params[] =
{
(void*)i,
(void*)this->size,
(void*)this->data[i]
};
function(params);
}
}

static void Extract(void** params, int& elementId, int& elementSourceDataLength, T& elementValue)
{
elementId = (int)params[0];
elementSourceDataLength = (int)params[1];
elementValue = (T)params[2];
}
};
#endif

and the main program code:

Code:
// main.cpp
#include <cstdio>
#include <cstdlib>
#include "DataBlock.h"


void FunctionOne(void** params)
{
int elementId, elementSourceDataLength, elementValue;
DataBlock<int>::Extract(params, elementId, elementSourceDataLength, elementValue);
fprintf(stdout, "%04d/%04d = %d\n", elementId, elementSourceDataLength, elementValue);
}

void FunctionTwo(void** params)
{
int elementId, elementSourceDataLength; char elementValue;
DataBlock<char>::Extract(params, elementId, elementSourceDataLength, elementValue);
fprintf(stdout, "%04d/%04d = %c\n", elementId, elementSourceDataLength, elementValue);
}

void FunctionThree(void** params)
{
int elementId, elementSourceDataLength; const char* elementValue;
DataBlock<const char*>::Extract(params, elementId, elementSourceDataLength, elementValue);
fprintf(stdout, "%04d/%04d = %s\n", elementId, elementSourceDataLength, elementValue);
}

int main(int argc, char* argv[])
{
int numbers[] = { 10, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
char letters[] = { 'H','e','l','l','o',' ','W','o','r','l','d' };
const char* strings[] = {"This","is","a","test","of","strings"};

DataBlock<int> numberData(numbers, (int)(sizeof(numbers)/sizeof(int)));
DataBlock<char> letterData(letters, (int)(sizeof(letters)/sizeof(char)));
DataBlock<const char*> stringData(strings, (int)(sizeof(strings)/sizeof(const char*)));

numberData.ForEach(FunctionOne);
letterData.ForEach(FunctionTwo);
stringData.ForEach(FunctionThree);

while('\n' != fgetc(stdin));

return 0;
}



I hope that you learned something here.
If anything doesn't make sense, feel free to ask questions so I can answer them! Cool
Logged

RedSlash
Offline Offline

Respect: 10
« Reply #1 on: March 28, 2010, 02:48:28 PM »

A nice attempt, but IMO more complicated than std::for_each which is much easier to use and is STL compatible.

Code:
#include <algorithm>
#include <stdio.h>

void printNumber(int& n)
{
        printf("%d\n",n);
}

int main(int argc,char *argv[])
{
        int numbers[] = { 1,2,3,4,5,6,7,8,9,10 };
        std::for_each(numbers,numbers + sizeof(numbers) / sizeof(int),printNumber);
        return 0;
}


-- output --
1
2
3
4
5
6
7
8
9
10

P.S. Your code does not compile on 64-bit mac.
Logged
Richard Marks
Administrator
Offline Offline

Respect: 3425
« Reply #2 on: March 28, 2010, 04:00:40 PM »

A nice attempt, but IMO more complicated than std::for_each which is much easier to use and is STL compatible.

I know that, and I already know about the STL implementation.
The purpose was to show how you can implement the functionality yourself without using the STL. Smiley

Quote
P.S. Your code does not compile on 64-bit mac.

Wait, what doesn't compile?
I use 64-bit machines, and have no issues building it..
Must be a crapintosh problem. Grin
Logged

RedSlash
Offline Offline

Respect: 10
« Reply #3 on: March 28, 2010, 04:17:58 PM »

Except that I find your solution significantly worse than the STL's. No offense.

Code:
template<typename _InputIterator, typename _Function>
    _Function
    for_each(_InputIterator __first, _InputIterator __last, _Function __f)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_requires_valid_range(__first, __last);
      for ( ; __first != __last; ++__first)
        __f(*__first);
      return __f;
    }


Quote
Must be a crapintosh problem.

Doesn't work on 64-bit Linux neither.

The reason it doesn't compile is the unportable code you have here:
elementId = (int)params[0];

You are downcasting a 64-bit (void *) to a 32-bit (int). You assumed that int was 64-bit, which is standard on 64-bit Windows but not on macs and linux.
Logged
Richard Marks
Administrator
Offline Offline

Respect: 3425
« Reply #4 on: March 28, 2010, 09:04:45 PM »

The reason it doesn't compile is the unportable code you have here:
elementId = (int)params[0];

You are downcasting a 64-bit (void *) to a 32-bit (int). You assumed that int was 64-bit, which is standard on 64-bit Windows but not on macs and linux.

You mean it doesn't yield the correct results then.
It should compile fine.

But yeah, I wasn't aware of an issue with a 64bit cast to a 32bit var.
I have never read any documentation on 64-bit compatibility issues.

So disclaimer: My implementation is 32-bit. Tongue
Logged

RedSlash
Offline Offline

Respect: 10
« Reply #5 on: March 28, 2010, 09:11:57 PM »

No, I actually mean it does not compile. GCC complains about it on 64-bit Mac and Linux.
Logged
Tags:
Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines
.: Theme by Richard Marks :.
Valid XHTML 1.0! Valid CSS!