As others have said, there are a few things to keep in mind, mainly make sure there is only one function definition.
I don't particularly like this solution but here it is.
One header to rule them all (pair.h)
#ifndef TEMPLATE_PAIR
#define TEMPLATE_PAIR
#include <stdlib.h>
#define PAIR_NAME( T1, T2 ) T1##T2##NAME
#define PAIR_PTR_NAME( T1, T2 ) T1##T2##PTR_NAME
#define PAIR_CREATE( T1, T2) MAKE##T1##T2
#define PAIR_PTR_CREATE( T1, T2) MAKE_PTR##T1##T2
#define PAIR_PTR_FREE(T1, T2) FREE##T1##T2
#define PAIR_DEFINITION( T1, T2) \
typedef struct { \
T1 first; \
T2 second ; \
} PAIR_NAME(T1, T2)
#define PAIR_PTR_DEFINITION( T1, T2) \
typedef struct { \
T1* first; \
T2* second ; \
} PAIR_PTR_NAME(T1, T2)
#define MAKE_PAIR_DECLARE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_DECLARE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define PAIR_PTR_FREE_DECLARE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )
#define MAKE_PAIR_SIGNATURE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_SIGNATURE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define FREE_PAIR_PTR_SIGNATURE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )
#define MAKE_PAIR_DEFINE( T1, T2 ) \
MAKE_PAIR_SIGNATURE(T1, T2) { \
PAIR_NAME(T1, T2) pair; \
pair.first = V1; \
pair.second = V2; \
return pair; \
}
#define MAKE_PAIR_PTR_DEFINE( T1, T2 ) \
MAKE_PAIR_PTR_SIGNATURE(T1, T2) { \
PAIR_PTR_NAME(T1, T2) pair; \
pair.first = malloc( sizeof(T1) ); \
if ( pair.first != 0 ) *(pair.first) = V1; \
pair.second = malloc( sizeof( T2) ) ; \
if ( pair.second != 0 ) *(pair.second) = V2; \
return pair; \
}
#define PAIR_PTR_FREE_DEFINE( T1, T2 ) \
FREE_PAIR_PTR_SIGNATURE(T1, T2) { \
free( Pair.first ); \
free( Pair.second ); \
}
#endif
One header to bring them all (defs.h):
#ifndef DEFS_HEADER
#define DEFS_HEADER
#include "pair.h"
typedef int* pInt;
PAIR_DEFINITION( int, int );
PAIR_DEFINITION( int, double );
PAIR_DEFINITION( double, double );
PAIR_DEFINITION( pInt, pInt );
PAIR_DEFINITION( float, int );
PAIR_PTR_DEFINITION( int, int );
MAKE_PAIR_DECLARE( int, int );
MAKE_PAIR_DECLARE( int, double );
MAKE_PAIR_DECLARE( double, double );
MAKE_PAIR_DECLARE( pInt, pInt );
MAKE_PAIR_DECLARE( float, int );
MAKE_PAIR_PTR_DECLARE( int, int );
PAIR_PTR_FREE_DECLARE( int, int );
#endif
And in the darkness bind them (impl.c):
#include "defs.h"
MAKE_PAIR_DEFINE( int, int );
MAKE_PAIR_DEFINE( int, double );
MAKE_PAIR_DEFINE( double, double );
MAKE_PAIR_DEFINE( pInt, pInt );
// manual "instantiation"
MAKE_PAIR_SIGNATURE( float, int )
{
PAIR_NAME( float, int ) local;
local.first = V1;
local.second = V2;
return local;
}
MAKE_PAIR_PTR_DEFINE( int, int );
PAIR_PTR_FREE_DEFINE( int, int );
In the land of main where the shadows lie:
#include "defs.h"
int main(void)
{
PAIR_NAME(int, int) myPairInts;
PAIR_NAME( double, double) myPairDoubles;
PAIR_NAME( pInt, pInt) myPairPointers;
PAIR_NAME( float, int) myPairOther;
PAIR_PTR_NAME( int, int ) pairPtr;
myPairInts = PAIR_CREATE( int, int ) (1, 2);
myPairDoubles = PAIR_CREATE( double, double ) (5, 6.5);
myPairPointers = PAIR_CREATE( pInt, pInt) ( 0, 0 );
myPairOther = PAIR_CREATE( float, int) (1, 1);
pairPtr = PAIR_PTR_CREATE(int, int) (1, 2 );
PAIR_PTR_FREE(int, int) (pairPtr );
return 0;
}
PAIR_NAME
creates a structure containing values, PAIR_PTR_NAME
contains pointers to values. PAIR_CREATE
and PAIR_PTR_CREATE
create values and fill the data inside the pair.
You will need to define all the necessary combinations in "impl.c". Any compilation unit can #include "defs.h"
and use the pairs.
EDIT - Answers to questions:
- "Wouldn't this cause trouble when I use this once in a library or something of that kind and then again in a program which uses both that library and the pair "template"?"
"pair.h" contains only macros, it can be safely used in any library or program.
What's important is that you don't define the same function twice. I wouldn't define the same structure twice either.
You can do the following though:
- take pair.h, defs.h and impl.c as they are above and build them into a library (add any necessary __declspec(dllexport)
and __declspec(dllimport
)
- you can then #include pair.h
and defs.h
and use the pairs defined there in a program
- if you want to use new pairs, say for (float, float)
you will need to add a new defs_my_program.h
and a new impl_my_program.c
to declare and define these new pair. The defs_my_program.h
header can be included along with the defs.h
header the library provides.
You still get one declaration and one definition for each pair. The only "downside" is that you can't really (safely) use pairs on-the-fly, they need to be centralized.
- "The way you chose the type and function names to be does bring some issues with it. You split up a pair with two values and one with two pointers. You would also need a specialisation for a pair with one pointer and one value, and for a pair with one value and one pointer. So I would have 4 cases of pairs already. Using this for triplets or even higher tuples, I would have to implement 2^n cases."
Well, for starters, you asked for std::pair
and not for a touple.
Note that std::pair
is the equivalent of PAIR_NAME
, there is no std::pair
that allocates dynamic memory.
You don't need any new specializations if you don't need to automatically use malloc
. The example with pInt
shows that you can create s pair
of (int, int*)
or (int*, int)
. It's just that the value of the pointer needs to come from outside the pair
.
If you really want a pair
of (int, int*)
that automatically allocates memory for the int*
you will have to add it yourself if you actually need it. I hope you will not.
For tuples of values, a not very optimal solution could be to use a pair
of a pair
and another element. This would give you a structure containing three elements. Something like that can probably be done via macro magics, cutting down on that exponential growth you are worried about.