Wouldn't a better scheme be to
i) allocate an object SelectItem for each item being listened to.
ii) store the pointer to the object in event.data.ptr
iii) extract that value when epoll_wait returns

and avoid the need to allocate or update the epfdtbl table.

I suspect you might need to be careful about the lifetime of the pointer (e.g., if it gets removed just after wait has responded), which might make it more problematic.  I can't immediately see a way around the issue....