I've created an example for cases where the types can vary per instance of the s-function.
The core of the s-function is contained in a C code file and header.
This C file is shared between simulation and code generation.
This code recieves attributes of the input and output ports using a structure.
Based on the contents of these attribute structures the core code dispatches to the correct behavior for each instance.
The wrapper C code and TLC code for the s-function need to fill in the structures,
then call the core function.
How the attribute structures are filled in is distinct to C and TLC.
But the core all comes from the same C file and header.
The example shows one possible way to organize the port information and do the dispatching.
Using the same concept, use whatever specific way works best for your application.