You could just implement a delegate wrapping that logic. Example implementation:
class KCallableWithInstance<out T>(private val func: KCallable<T>, private val instance: Any) : KCallable<T> by func {
private val instanceParam = func.instanceParameter ?:
func.extensionReceiverParameter ?:
throw IllegalArgumentException("Given function must not have a instance already bound")
init {
val instanceParamType = instanceParam.type.jvmErasure
if (!instance::class.isSubclassOf(instanceParamType))
throw IllegalArgumentException(
"Provided instance (${instance::class.qualifiedName}) isn't an subclass of " +
"instance param's value's class (${instanceParamType::class.qualifiedName})")
}
override fun call(vararg args: Any?): T
= func.call(instance, *args)
override fun callBy(args: Map<KParameter, Any?>): T
= func.callBy(args + (instanceParam to instance))
override val parameters = func.parameters.filter { it != instanceParam }
}
fun <T> KCallable<T>.withInstance(instance: Any): KCallable<T>
= KCallableWithInstance(this, instance)
And then use it like this (example based on the code in question): func.withInstance(instance).call(1, "Hi", 123.45)