简体   繁体   中英

How to make std::map compare to work on multiple data types?

I've a entire table stored in std::deque<record *> and I need to allow the user to sort the table on any column. The table is presented to the user in a list box format.

Each record consists of multiple strings (struct of strings). However, the fields are of different types ie, time (HH:MM:SS), float, and strings, even though they are all stored as strings.

The user is permitted to sort on any of these columns. When the user clicks on the column, I store each record in a multimap so that the table is shown in sorted format to the user.

However, since the columns are of different types, how do I write a single compare method, that handles all these efficiently?

I thought of the following ways

  1. Use different maps for each type and write one compare function class for each of the maps.
  2. Use a single map, with a compare class that handles all three different types. But for each insertion, the comparison class has to decide the type , and insert accordingly.

Is there a better way than these two?

Example:

struct ltDataCompare
{

    bool operator()( const CString& csData1, const CString& csData2)  const
    {

        if ( isTimeFormat(csData1) && isTimeFormat(csData1) )
        {
               // Do time relevant comparision
            }
            else if ( isNumberFormat( csTime1 ) && isNumberFormat(csTime2) )
        {
            double dPrice1 = atof((LPCTSTR)csTime1);
            double dPrice2 = atof((LPCTSTR)csTime2);

            return ( dPrice1 < dPrice2);
        }
        return ( csTime1 < csTime2 );
    }
};

std::multimap<CString,list_record_t*,ltDataCompare> _mapAllRecords; // Used only for sorting

You can't re-sort a map or multimap - once an item is inserted, its position is locked. It would be better to use a different container such as a vector and sort it when necessary.

The nice thing about a comparison class is that it is allowed to contain state. You can have a member with some constant or pointer to determine which comparison method to use.

You can use the same principle to choose which field to sort on.

struct ltDataCompare 
{
    ltDataCompare(int field, int method) : m_field(field), m_method(method) {}
    bool operator()( const record& left, const record& right) const 
    {
        if (m_method == enumTimeFormat)
            return CompareTimes(left[m_field], right[m_field]);
        else if (m_method == enumNumberFormat)
            return CompareNumbers(left[m_field], right[m_field]);
        // ...
    }
    int m_field;
    int m_method;
};

std::sort(table.begin(), table.end(), ltDataCompare(0, enumTimeFormat));

You could be more elegant about it - I don't know you'd save yourself any work - if you had a class with a < operator in it for each of the types. If you have a superclass that has a virtual < operator then you can use it as the key type, as in

std::multimap< superclass, list_record_t >

Now you can use any of the child types as the actual keys (so long as you remain consistent). Actually I'm not sure whether this is more clever or more elegant. More clever is generally a bad thing (as it means more obscure/less maintainable). If it makes for fewer lines of code, that's usually a good thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM