0

I want to switch between comparer in qsort instead creating 5 different comparer, but for some reason the program doesn't work. I don't have any idea why this is happen. Can anyone explain please? Thanks!

struct propertySales{
    unsigned int tanggal[3];
    char pelanggan[30];
    char jenisProperty[30];
    char namaProperty[30];
    int jumlahProperty;
    double hargaProperty;
    double totalPembayaran;
}Arr[100], compare[100], temp;
typedef int (*compfn)(const void*, const void*);
int compare_harga(struct propertySales *, struct propertySales *);
int compare_pembayaran(struct propertySales *, struct propertySales *);
int compare_harga(struct propertySales *elem1, struct propertySales *elem2){
    if( elem1->hargaProperty < elem2->hargaProperty){
        return -1;
    }
    else if(elem1->hargaProperty > elem2->hargaProperty){
        return 1;
    }
    else{
        return 0;
    }   
}

int compare_pembayaran(struct propertySales *elem1, struct propertySales *elem2){
    if( elem1->totalPembayaran < elem2->totalPembayaran){
        return -1;
    }
    else if(elem1->totalPembayaran > elem2->totalPembayaran){
        return 1;
    }
    else{
        return 0;
    }   
}

The compiler throw warning saying --- [Warning] cast to pointer from integer of different size [-Wint-to-pointer-cast]

  • 1
    First, the parameters to the comparison functions have to be `const void *`, then cast to point to the known struct type within the function(s)... Second, (having seen a sample yesterday), please show the declarations of the structs as they are today. (I remember the members were pointers yesterday, so more dereferencing would be needed...) Third, `else` after a `return` is needless decoration... – Fe2O3 Jan 12 '23 at 20:54
  • 2
    The warning you give is related to the code you edited out. – dbush Jan 12 '23 at 20:59
  • @Fe2O3 sorry, I show the struct there. Just fyi, this is my mini project for college, and I'm pretty stupid about pointers haha. Can you gimme example about the first thing you mention, I'm still lost about that. Thanks for the correction. – Raynaldy Dwi Jan 12 '23 at 21:05
  • 1
    Surely the diagnostic you present is accompanied by an indication of exactly what conversion the compiler is talking about. – John Bollinger Jan 12 '23 at 21:07
  • @RaynaldyDwi The answer to your previous question has such an example. – dbush Jan 12 '23 at 21:07
  • You want to do The Wrong Thing. Just have five different comparers. Sorry. – Dúthomhas Jan 12 '23 at 21:08
  • @Dúthomhas so the right thing is just creating 5 different comparers?? – Raynaldy Dwi Jan 12 '23 at 21:14
  • Yes. Discriminate between comparers before you invoke `qsort()`, not during the sorting process. – Dúthomhas Jan 12 '23 at 21:17
  • @Dúthomhas Good eyes! I overlooked the first words of the OP's question and presumed they were using qsort() correctly (having selected the desired comparison function ahead of time...) "Never assume..." Hard lesson to keep forefront in one's mind. – Fe2O3 Jan 12 '23 at 21:22
  • @RaynaldyDwi " but for some reason the program doesn't work." --> post the calling code too, Post a[mcve]. – chux - Reinstate Monica Jan 12 '23 at 22:06
  • @RaynaldyDwi " but for some reason the program doesn't work." --> Post the input, what you saw, what you expected. – chux - Reinstate Monica Jan 12 '23 at 22:07
  • It is possible to reduce a lot of your typing by using generics and the `offsetof` macro... I’m headed out to an activity at the school with my daughter, but when I get back I can type up an example. – Dúthomhas Jan 12 '23 at 22:35
  • I’m back. I typed up an example. It was way more macro boilerplate than strictly necessary. And I learned that `qsort_s()`/`qsort_r()` is a cross-plaform nightmare. Just do it the straight-forward way and avoid needing any state or generic magic with your own types. – Dúthomhas Jan 14 '23 at 03:03

2 Answers2

0

Short code

Let’s start with our structures. I’m going to mess with them, just for the purposes of having a greater variety of object types inside.

// property-sales.h

struct Tanggal { // date
    short tahun; // year
    char  bulan; // month
    char  hari;  // day
};
typedef struct Tanggal Tanggal;

struct Penjualan_Properti {
    Tanggal      tanggal_penjualan;   // date of sale      // two different ways
    time_t       tanggal_pembelian;   // date of purchase  // to manage a date
    char         jalan[32];           // street name
    int          nomor;               // number
    const char * kelurahan;           // subdistrict       // These are strings kept 
    const char * kecamatan;           // district          // elsewhere, selected from
    const char * kota_atau_kabupaten; // city or regency   // a list.
    const char * propinsi;            // province          //
    char         kode_pos[6];         // zip code
    double       luasnya_km;          // area (km)
    double       harga;               // price
    double       total_pembayarn;     // total payment to date
    Pelanggan  * pelanggan;           // customer
    char         keadaan;             // status   'U'=tertunda, 'J'=terjual, 'A'=arsip
};
typedef struct Penjualan_Properti Penjualan_Properti;

// Precision for floating point comparisons can be controlled
extern double epsilon;

Now for comparator functions.

// property-sales.c

#include "property-sales.h"

// Precision for floating point comparisons
double epsilon = 0.00001;

// Now we just list a comparator function for every kind of sort order we want

int membandingkan_nomor_Penjualan_Properti( const void * _a, const void * _b ) {
    // Sort non-decreasing by building number
    const Penjualan_Properti * a = _a;
    const Penjualan_Properti * b = _b;
    return (*a < *b) ? -1 : (*b < *a);
}

int membandingkan_kode_pos_Penjualan_Properti( const void * _a, const void * _b ) {
    // Sort non-decreasing by building number
    const Penjualan_Properti * a = _a;
    const Penjualan_Properti * b = _b;
    return strcmp( a->kode_pos, b->kode_pos );
}

// ...and so on... (for as many ways as you wish to sort two Property_Sales objects)

That’s it! Now to sort your data you just need to use the proper comparator function!

qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_kode_pos_Penjualan_Properti );

In English:

qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_zip_code );

Shorter code with X-macros

You can reduce the repetative typing quite a bit with some macros, but the real benefit here is ODR, which is a useful concept in C as well as C++.

The idea is to create a single table of information that we will afterward use to construct our objects.

// property-sales.h

#include <time.h>

#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAY_SIZE(arr) (IS_ARRAY(arr) ? (sizeof(arr) / sizeof(arr[0])) : 0)

struct Tanggal {
    short tahun;   // year
    char  bulan;   // month
    char  hari;    // day
};
typedef struct Tanggal Tanggal;

typedef struct Pelanggan { int foo; } Pelanggan;  // Client struct stub defintion

//     type,         name,         dimensions, comparator function code
#define PENJUALAN_PROPERTI(X) \
    X( Tanggal,      tanggal_penjualan,      , (a->tahun < b->tahun) ? -1 : (a->tahun < b->tahun) ? 1 :   \
                                               (a->bulan < b->bulan) ? -1 : (a->bulan < b->bulan) ? 1 :   \
                                               (a->hari  < b->hari)  ? -1 : (a->hari  < b->hari);       ) \
    X( time_t,       tanggal_pembelian,      , (*a < *b) ? -1 : (*b < *a) ) \
    X( char,         jalan,              [32], strcmp( a, b ) ) \
    X( int,          nomor,                  , (*a < *b) ? -1 : (*b < *a) ) \
    X( const char *, kelurahan,              , strcmp( *a, *b ) ) \
    X( const char *, kecamatan,              , 0 ) /* see note below */ \
    X( const char *, kota_atau_kabupaten,    , strcmp( *a, *b ) ) \
    X( const char *, propinsi,               , strcmp( *a, *b ) ) \
    X( char,         kode_pos,            [6], strcmp(  a,  b ) ) \
    X( double,       luasnya_km,             , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( double,       harga,                  , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( double,       total_pembayarn,        , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( Pelanggan *,  pelanggan,              , membandingkan_Pelanggan( *a, *b ) ) \
    X( char,         keadaan,                , status_cmp( *a, *b ) ) \

// Create the struct!
#define F(type, name, extents, code) type name extents;
struct Penjualan_Properti { PENJUALAN_PROPERTI(F) };
typedef struct Penjualan_Properti Penjualan_Properti;
#undef F

// Create comparator function prototypes
#define F(type, name, extents, code) \
    int membandingkan_ ## name ## _Penjualan_Properti( const void * _a, const void * _b );
PENJUALAN_PROPERTI(F)
#undef F

// Precision for floating point comparisons can be controlled
extern double epsilon;

// Order of sorting by status code can be controlled as well:
extern char status_order[4];  // 'U'=tertunda, 'J'=terjual, 'A'=arsip

Woah! Yeah! Now to generate the comparator functions:

// property-sales.c

#include <stddef.h>
#include <string.h>
#include "property-sales.h"

// Precision for floating point comparisons
double epsilon = 0.00001;

// Helpers to sort status code as we desire. Defaults to:
char status_order[4] = "UJA";

int status_cmp( char _a, char _b )
{
    int a = (int)(strchr( status_order, _a ) - status_order);
    int b = (int)(strchr( status_order, _b ) - status_order);
    return (a < b) ? -1 : (b < a);
}

// Also, that compare_client() function needs to exist
int membandingkan_Pelanggan( const Pelanggan * a, const Pelanggan * b ) {
    return 0;  // Change this to something that properly sorts by client.
               // Since a Pelanggan is a struct, feel free to use any
               // dirty trick you like to compare them by a specific element.
               // For example, a `client_order` global may exist that
               // is itself just a function pointer to a `compare_Client_name`
               // function or a `compare_Client_district` function, etc, which
               // we could use here as:
               //   return client_order( a, b );
}

// Generate the comparator functions!
#define F(type, name, extents, code) \
    int membandingkan_ ## name ## _Penjualan_Properti( const void * _a, const void * _b ) { \
        type const * a = (const void *)((const char *)_a + offsetof(Penjualan_Properti,name)); \
        type const * b = (const void *)((const char *)_b + offsetof(Penjualan_Properti,name)); \
        return code; \
    }
PENJUALAN_PROPERTI(F)
#undef F

That was... surprisingly short, right? Even with all the additional code doign stuff the original code could not, and complete code for all comparator functions (because we left them all out in the first example).

The obvious tradeoff is that now we are in tricky-code territory.
The original code is very clear and easy to read. This stuff takes extra brainpower.

Whether it is worth it or not is a debate to be had elsewhere. For now, we can easily sort an array of Property_Sales against any member.

strcpy( status_order, "AUJ" ); // sort by status: Archived, Pending, Sold
qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_keadaan_Penjualan_Properti );

English:

strcpy( status_order, "APS" ); // sort by status: Archived, Pending, Sold
qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_status );

If that Client structure existed, and had things set up to sort Clients by a specific member comparator function, then you could sort by client as well:

pelanggan_order = &membandingkan_nama_keluarga_Pelanggan;
qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_pelanggan_Penjualan_Properti

English:

client_order = &compare_Client_surname;
qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_client );

That’s it!

All the code is still repetitive, but we put all that repetition into some macros to generate code for us from a table. This trick is known as the X-macro trick.

Noted from above: we made the district effectively unsortable by simply returning a 0 value. We could have should have given it sort ability just like all the other elements around it. The point is just to provide an example!

I should point out that I also used the (in)famous offsetof() macro to make things nicer for us. In the sort function a and b are now direct pointers to the member elements to compare (and not to the structure itself as before). Notice how this had a direct effect on the comparison code in the PENJUALAN_PROPERTI table: be careful to remember at what level of indirection you are at. Notice, for example, the difference when using strcmp() on a member string array versus a member string pointer.

Remember also that qsort() is neither re-entrant nor stable. Playing with global state like we are means we need to be careful about threaded and recursive uses.

Oh, and find that ARRAY_SIZE macro here (with a slightly different name).

Remember, with great power comes great responsibility!

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39
-1

OP requested example of longer block of code to illustrate usage.

(No warranty that this is suitable as is.)

int compare_pembayaran( const void *p1, const void *p2 ) {
    const struct propertySales *elem1 = p1, *elem2 = p2;

    if( elem1->totalPembayaran < elem2->totalPembayaran)
        return -1;
    
    return elem1->totalPembayaran > elem2->totalPembayaran;
}
Fe2O3
  • 6,077
  • 2
  • 4
  • 20
  • " It may be better to check that the difference exceeds (or not) some small threshold." (or epsilon) with `qsort()` is not possible. Consider `a, b` area close so they compare equal, same for `b, c`. Yet `a, c` are far enough apart to be `a > c`. This breaks `qsort()` as `a` does not compare equal to `c`. – chux - Reinstate Monica Jan 12 '23 at 22:15
  • Sorry, I've edit the comment a couple times. Please re-read comment and this too: "When the same objects (consisting of size bytes, irrespective of their current positions in the array) are passed more than once to the comparison function, the results shall be consistent with one another. That is, for qsort they shall define a total ordering on the array". C17dr § 7.22.5 4Still any question? – chux - Reinstate Monica Jan 12 '23 at 22:17
  • 1
    FYI: Further compare [woes](https://stackoverflow.com/q/48069404/2410359) with FP and NAN. – chux - Reinstate Monica Jan 12 '23 at 22:21