It's possible with _Generic but there's a lot of caveats. Any seasoned C dev would just have two functions with a prefix or suffix, like cat_greet() and dog_greet().
But if you insist, it would look something like:
typedef ... cat;
typedef ... dog;
void cat_greet(cat *obj) {/*do stuff */}
void dog_greet(dog *obj) {/*do stuff */}
#define greet(X) _Generic((X), cat: cat_greet, dog: dog_greet)(X)
int main(...)
{
cat mycat;
dog mydog;
greet(&mycat);
greet(&mydog);
return 0;
}