2

I want to implement a probability space with a density wrt R^n as follows:

typedef struct _ProbabilitySpace {
  int dim;
  double (*density)(double *first, double *last);
  /** more stuff **/
} ProbabilitySpace;

Now, with this implementation, how would it be possible to implement a family of density functions? Lets say, I want to implement the gaussian family, then I could write a function

double gaussian(struct vec mu, struct mat sigma, double *first, double *last){
  /** gaussian density function here */
}

As you see, this density depends not only on the arguments *first and *last, but also on mu and sigma. I want to give the user the possibility, to specify mu and sigma once (probably at runtime), without having to pass it around all the time. Also, this function might be used for different combinations of (mu, sigma) in different ProbabilitySpace objects.

A solution in C++ would be to create another class like this:

class _Density{
  public:
    double evaluate(double* first, double* last);
  private: 
    /** any private parameters **/
};

Any family of probability distributions could inherit from this class and define private parameters, which the user must initialize in the constructor. The evaluate member function could then access these private parameters.

But in C, I don't see any way to specify parameters other than in the parameter-list for any function, as they cannot access other member variables.

Maybe someone can suggest a solution?

2
  • 1
    Put all "class members" in a struct, put all related "class methods" in one C source file (translation unit), specify a pointer to the struct as their 1st parameter. Define "private" functions static, all others get prototyped in the related C header. If you want to emulate "protected" functions this gets a bit complicated ;-) Write a c/tor function and d/tor function for the struct. Commented Mar 26, 2016 at 9:31
  • Basically a similar solution as the one which is accepted... thank you Commented Mar 26, 2016 at 10:20

3 Answers 3

2

Any family of probability distributions could inherit from this class and define private parameters, which the user must initialize in the constructor. The evaluate member function could then access these private parameters

You see, C++ every time passes a hidden parameter this to member function evaluate, which allows to use those private parameters. In C you have to do this yourself. Kind of

struct _Density{
  /** any private parameters **/
} d;

InitDensity(&d, ...); /* constructor */
gaussian(&d, first, last);
/* etc. */

Actually, C++ compiler does pretty same work for you automatically.

Sign up to request clarification or add additional context in comments.

2 Comments

I think this might actually work, although passing the address of the struct object to its member function pointer when it gets invoked seems a bit technical... I'll try and report back, if I encounter any issues.
Yes, all that can be made in C++ should be doable in C, as first C++ compilers were generating C as a first step and then compiling generated C code.
0

Your C++ solution wouldn't work either, because pointer to member function_Density::evaluate is not compatible with the double (*density)(double *first, double *last) function pointer type.

The reason for this incompatibility is that non-static member functions are taking an extra parameter this implicitly. The compiler hides the presence of that parameter, but it is there.

The solution in C++ is to use std::function in place of function pointers. However, cannot do the same in C, so idiomatic solution is to pass an extra parameter of type void* to all functions in the family, and let them "crack" the parameter internally, extracting whatever they need:

typedef struct _ProbabilitySpace {
  int dim;
  double (*density)(double *first, double *last, void *user_data);
  /** more stuff **/
} ProbabilitySpace;

typedef struc {
    struct vec mu;
    struct mat sigma;
} GaussianData;

double gaussian(double *first, double *last, void *user_data){
    GaussianData *data = (GaussianData*)user_data;
    ...
}

This approach unifies the function signature, letting all kinds of parameters to be passed to a function called by pointer. You need to make sure that these parameters remain valid at the time the function pointer may access them, for example, that you are not storing a pointer to local that is about to go out of scope.

3 Comments

Well, of course in c++ I would have to replace the function pointer with a class(pointer) to the ProbabilityDensity object...
Regarding your solution, I would still have to pass around the user data all the time, which makes it somewhat inconvenient
@user3617992 You could, but you wouldn't have to if you use std::function. As far as convenience goes, there's nothing convenient about C. As the old saying goes, C combines the speed of an assembly language with the expressive power of an assembly language.
0

You can do it the following way

typedef struct _ProbabilitySpace {
  int dim;
  double (*density)(double *first, double *last);
  /** more stuff **/
} ProbabilitySpace;

//...
double gaussian( double *first, double *last )
{
    //... 
    gaussian_impl( /*...*/ );
}

static double gaussian_impl(struct vec mu, struct mat sigma, double *first, double *last){
  /** gaussian density function here */
}

//...

ProbabilitySpace p;

p.density =  gaussian;

3 Comments

I don't see how this solution would help me, since now I would have to hard code mu and sigma parameters in the function gaussian... the hard coding is the problem, which I can't get around.
@user3617992 I am sorry/ I do not understand. You already showed the functio n declaration/ So what is the problem?
well, from the implementation of the gaussian density family I wanted to somehow derive functions where the arguments mu and sigma are substituted with fixed numbers, so that I could use them in the _ProbabilitySpace struct without specifying these parameters every time. The problem was that there are indefinitely many possibilities for mu and sigma, so I couldn't just hard code them into the source.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.