La solution I Mis en œuvre
Le consensus était que cela pourrait se faire. J'ai en quelque sorte adapté le concept type erasure, avec les suggestions des autres ci-dessus. J'ai quelque chose qui fonctionne maintenant. La clé de la carte doit être un objet qui a un pointeur vers un objet clé polymorphe.
J'ai essayé d'utiliser uniquement l'objet de base comme type de clé, mais lorsque la carte crée sa copie de la clé, il semble qu'elle ne faisait que copier la classe de base.
Donc je suis passé naïvement à un pointeur (key_base_c *
). Cependant, ceci a juste fait la comparaison de pointeur. Mon tri n'était même pas utilisé. Après la lecture des informations type erasure. J'ai adapté ma solution de pointeur en la plaçant à l'intérieur d'un objet multi_key_c
qui a transmis ses appels <
, ==
et strIdx()
au pointeur key_base_c
que j'ai caché à l'intérieur de celui-ci. Après avoir écrit quelques classes dérivées, j'ai vite vu que cela se prêtait à être un modèle et que ma solution se mettait rapidement en place.
Je pense qu'il peut y avoir de meilleures façons de mettre en œuvre, mais voici ce que j'ai jusqu'à présent:
#include <map>
#include <sstream>
#include <iostream>
#include <utility>
//
// list of types to act as the primary key. The primary key dicatates the
// format of the secondary key.
//
enum e_types {
E_ERROR = 0,
E_INT = 1,
E_CHAR = 2,
E_STR = 3,
};
// Base class for the multi-key.
class key_base_c {
public:
key_base_c (enum e_types key_type) :
key1(key_type)
{};
virtual ~key_base_c(void) {};
virtual std::string strIdx (void) const {
std::stringstream ss_idx;
ss_idx << key1;
return (ss_idx.str());
}
virtual bool operator< (const key_base_c &b) const {
return (key_base_c::operator<(&b));
}
virtual bool operator< (const key_base_c *p) const {
return (key1 < p->key1);
}
virtual bool operator== (const key_base_c &b) const {
return (key_base_c::operator==(&b));
}
virtual bool operator== (const key_base_c *p) const {
return (key1 == p->key1);
}
protected:
e_types key1; // the primary key
};
// template policy_key_c
//
// EVENT_TYPE_VAL - select the enumerated value to use for key1's value
//
// KEY2_TYPE - select the class to use for the second key. For built
// in types they use their default < and == operators,
// If a private custom type is specified then it must
// have its own < and == operators specified
//
template <enum e_types EVENT_TYPE_VAL,
class KEY2_TYPE>
class policy_key_c : public key_base_c
{
public:
policy_key_c (KEY2_TYPE key_value) :
key_base_c(EVENT_TYPE_VAL),
key2(key_value)
{};
virtual ~policy_key_c(void) {};
// return the index as a string.
virtual std::string strIdx (void) const {
std::stringstream ss_idx;
ss_idx << key_base_c::strIdx() << "." << key2;
return (ss_idx.str());
}
//
// operator <
//
virtual bool operator< (const key_base_c &b) const {
return (operator<(&b));
}
virtual bool operator< (const key_base_c *p) const {
// if the primary key is less then it's less, don't check 2ndary
if (key_base_c::operator<(p)) {
return (true);
}
// if not less then it's >=, check if equal, if it's not equal then it
// must be greater
if (!(key_base_c::operator==(p))) {
return (false);
}
// primary keys are equal, so now check the 2ndary key. Since the
// primary keys are equal then that means this is either a key_base_c
// object or its a policy_key_c object.
const policy_key_c *p_other = dynamic_cast<const policy_key_c*>(p);
// if NULL then it was a key_base_c, and has no secondary key, so it is
// lexigraphically smaller than us, ergo we are not smaller than it.
if (!p_other) {
return (false);
}
return (key2 < p_other->key2);
}
//
// operator ==
//
virtual bool operator== (const key_base_c &b) const {
return(operator==(&b));
}
virtual bool operator== (const key_base_c *p) const {
// if the primary key isn't equal, then we're not equal
if (!(key_base_c::operator==(p))) {
return (false);
}
// primary key is equal, so now check the secondary key. Since the
// primary keys are equal, then that means this is eitehr a key_base_c
// object or its a policy_key_c object.
const policy_key_c *p_other = dynamic_cast<const policy_key_c*>(p);
// if NULL then it was a key_base_c
if (!p_other) {
// why? If the LHS is a key_base_c it doesn't go any deeper than
// the base. Hence we don't either.
return (true);
}
return (key2 == p_other->key2);
}
protected:
KEY2_TYPE key2; // The secondary key.
};
class multi_key_c {
public:
multi_key_c (key_base_c *p) :
p_key(p)
{};
~multi_key_c(void) {};
bool operator< (const multi_key_c &mk) const {
return (p_key->operator<(mk.p_key));
}
bool operator== (const multi_key_c &mk) const {
return (p_key->operator==(mk.p_key));
}
std::string strIdx (void) const {
return (p_key->strIdx());
}
protected:
key_base_c *p_key;
};
// DO_TEST(x, op, y)
// x, y: can be any derived key type
// op : The operation to do < or ==
//
// Prints the operation being done along with the results of the operation
// For example:
// DO_TEST(a, <, b)
// will print:
// a < b: <results>
//
// where <results> are the results of the operation 'a < b'
#define DO_TEST(x, op, y, expect) \
{ \
bool retval = x op y; \
std::cout << #x " " #op " " #y ": " << retval \
<< " = " << ((retval == expect) ? "pass" : "----FAIL") << "\n"; \
}
template <class C>
void
print_them (C **pp_c,
int count,
std::string s_type)
{
int idx;
std::cout << "\n" << count << " keys for " << s_type << "\n";
for (idx = 0 ; idx < count; ++idx) {
std::cout << " " << (*pp_c)->strIdx() << "\n";
pp_c++;
}
}
int
main (void)
{
std::cout << "\nBASE\n";
key_base_c base_error(E_ERROR), base_int(E_INT), base_char(E_CHAR);
key_base_c base_str(E_STR);
key_base_c *key_base_array[] = {
&base_error, &base_int, &base_char, &base_str
};
print_them(key_base_array,
(sizeof(key_base_array)/sizeof(key_base_array[0])),
"key_base_c");
DO_TEST(base_error, < , base_error, false);
DO_TEST(base_error, < , base_int, true);
DO_TEST(base_int, < , base_char, true);
DO_TEST(base_char, < , base_str, true);
std::cout << "\n";
DO_TEST(base_error, ==, base_error, true);
DO_TEST(base_int, ==, base_int, true);
DO_TEST(base_char, ==, base_char, true);
DO_TEST(base_str, ==, base_str, true);
std::cout << "\n";
DO_TEST(base_error, ==, base_int, false);
DO_TEST(base_int, ==, base_char, false);
DO_TEST(base_char, ==, base_str, false);
// INT
//
typedef policy_key_c<E_INT, int> key_int_2;
key_int_2 i1(1), i2(2), i3(3), i4(4);
key_int_2 *key_int2_array[] = {
&i1, &i2, &i3, &i4,
};
print_them(key_int2_array,
(sizeof(key_int2_array)/sizeof(key_int2_array[0])),
"key_int_2");
DO_TEST(base_int, < , i1, false);
DO_TEST(i1, < , base_int, false);
DO_TEST(i1, < , base_char, true);
DO_TEST(base_char, < , i1, false);
DO_TEST(i1, ==, i1, true);
DO_TEST(i1, ==, base_int, true);
DO_TEST(base_int, ==, i1, true);
DO_TEST(i1, ==, base_error, false);
DO_TEST(base_error, ==, i1, false);
std::cout << "\n";
DO_TEST(i1, < , i2, true);
DO_TEST(i1, < , i3, true);
DO_TEST(i1, < , i4, true);
// CHAR
typedef policy_key_c<E_CHAR, char> key_char_c;
key_char_c c1('a'), c2('b'), c3('c'), c4('d');
key_char_c *key_char_array[] = {
&c1, &c2, &c3, &c4,
};
print_them(key_char_array,
(sizeof(key_char_array)/sizeof(key_char_array[0])),
"key_char");
DO_TEST(base_int, < , c1, true);
DO_TEST(base_int, ==, c1, false);
DO_TEST(base_char, < , c1, false);
DO_TEST(base_char, ==, c1, true);
DO_TEST(base_str, < , c1, false);
DO_TEST(base_str, ==, c1, false);
std::cout << "\n";
DO_TEST(c1, < , c1, false);
DO_TEST(c1, ==, c1, true);
DO_TEST(c1, < , c2, true);
DO_TEST(c1, ==, c2, false);
std::cout << "\n";
DO_TEST(c1, ==, i1, false);
DO_TEST(i1, ==, c1, false);
DO_TEST(c1, < , i1, false);
DO_TEST(i1, < , c1, true);
// STR
typedef policy_key_c<E_STR, std::string> key_str_c;
key_str_c s1("aaa"), s2("bbb"), s3("ccc"), s4("ddd");
key_str_c *key_str_array[] = {
&s1, &s2, &s3, &s4
};
print_them(key_str_array,
(sizeof(key_str_array)/sizeof(key_str_array[0])),
"key_str");
DO_TEST(base_int, < , s1, true);
DO_TEST(base_char, < , s1, true);
DO_TEST(base_str, < , s1, false);
DO_TEST(base_str, ==, s1, true);
DO_TEST(s1, < , base_int, false);
DO_TEST(s1, < , base_char, false);
DO_TEST(s1, < , base_str, false);
DO_TEST(s1, ==, base_str, true);
std::cout << "\n";
DO_TEST(s1, < , s1, false);
DO_TEST(s1, ==, s1, true);
DO_TEST(s1, < , s2, true);
DO_TEST(s1, ==, s2, false);
std::cout << "\n\nNOW TESTING THE MAP\n\n";
typedef std::multimap<multi_key_c, std::string> multiKeyMap;
multiKeyMap myMap;
multi_key_c k1(&i1), k2(&i2), k3(&i3), k4(&i4);
multi_key_c k5(&c1), k6(&c2), k7(&c3), k8(&c4);
multi_key_c k9(&s1), k10(&s2), k11(&s3), k12(&s4);
myMap.insert(std::make_pair(k1, "one"));
myMap.insert(std::make_pair(k2, "two"));
myMap.insert(std::make_pair(k3, "three"));
myMap.insert(std::make_pair(k4, "four"));
myMap.insert(std::make_pair(k1, "one.2"));
myMap.insert(std::make_pair(k4, "four.2"));
myMap.insert(std::make_pair(k5, "c1"));
myMap.insert(std::make_pair(k5, "c1.2"));
myMap.insert(std::make_pair(k6, "c2"));
myMap.insert(std::make_pair(k6, "c2.2"));
myMap.insert(std::make_pair(k7, "c3"));
myMap.insert(std::make_pair(k8, "c4"));
myMap.insert(std::make_pair(k9, "s1"));
myMap.insert(std::make_pair(k10, "s2"));
myMap.insert(std::make_pair(k11, "s3"));
myMap.insert(std::make_pair(k12, "s4"));
myMap.insert(std::make_pair(k12, "s4.2"));
myMap.insert(std::make_pair(k11, "s3.2"));
myMap.insert(std::make_pair(k10, "s2.2"));
myMap.insert(std::make_pair(k9, "s1.2"));
multiKeyMap::iterator pos;
for (pos = myMap.begin(); pos != myMap.end(); ++pos) {
std::cout << pos->first.strIdx() << " : " << pos->second
<<"\n";
}
return (0);
}
sortie de c'est:
BASE
4 keys for key_base_c
0
1
2
3
base_error < base_error: 0 = pass
base_error < base_int: 1 = pass
base_int < base_char: 1 = pass
base_char < base_str: 1 = pass
base_error == base_error: 1 = pass
base_int == base_int: 1 = pass
base_char == base_char: 1 = pass
base_str == base_str: 1 = pass
base_error == base_int: 0 = pass
base_int == base_char: 0 = pass
base_char == base_str: 0 = pass
4 keys for key_int_2
1.1
1.2
1.3
1.4
base_int < i1: 0 = pass
i1 < base_int: 0 = pass
i1 < base_char: 1 = pass
base_char < i1: 0 = pass
i1 == i1: 1 = pass
i1 == base_int: 1 = pass
base_int == i1: 1 = pass
i1 == base_error: 0 = pass
base_error == i1: 0 = pass
i1 < i2: 1 = pass
i1 < i3: 1 = pass
i1 < i4: 1 = pass
4 keys for key_char
2.a
2.b
2.c
2.d
base_int < c1: 1 = pass
base_int == c1: 0 = pass
base_char < c1: 0 = pass
base_char == c1: 1 = pass
base_str < c1: 0 = pass
base_str == c1: 0 = pass
c1 < c1: 0 = pass
c1 == c1: 1 = pass
c1 < c2: 1 = pass
c1 == c2: 0 = pass
c1 == i1: 0 = pass
i1 == c1: 0 = pass
c1 < i1: 0 = pass
i1 < c1: 1 = pass
4 keys for key_str
3.aaa
3.bbb
3.ccc
3.ddd
base_int < s1: 1 = pass
base_char < s1: 1 = pass
base_str < s1: 0 = pass
base_str == s1: 1 = pass
s1 < base_int: 0 = pass
s1 < base_char: 0 = pass
s1 < base_str: 0 = pass
s1 == base_str: 1 = pass
s1 < s1: 0 = pass
s1 == s1: 1 = pass
s1 < s2: 1 = pass
s1 == s2: 0 = pass
NOW TESTING THE MAP
1.1 : one
1.1 : one.2
1.2 : two
1.3 : three
1.4 : four
1.4 : four.2
2.a : c1
2.a : c1.2
2.b : c2
2.b : c2.2
2.c : c3
2.d : c4
3.aaa : s1
3.aaa : s1.2
3.bbb : s2
3.bbb : s2.2
3.ccc : s3
3.ccc : s3.2
3.ddd : s4
3.ddd : s4.2
Je veux assurez-vous de bien comprendre votre réponse. Je crois que vous dites que si je peux me permettre les multiples cartes, alors faites-le, car c'est rapide et facile. Donc, il semble que ce soit un cas où je suis trop excité à propos de ce que C++ peut faire et j'ai essayé d'en faire trop;) –
oui c'est correct. –