Unable to reference kotlinx.cinterop package in Kotlin Multiplatform project - kotlin

I created a Kotlin Multiplatform project based on the example for iOS and Android (https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html). Within my iOS target, I am using cinterop to link in a 3rd party Objective-C framework. This is working fine. I am able to reference the framework classes in my Kotlin code within the 'iosMain' source set. However, one of the method calls requires an NSError** parameter. I am attempting to use the following to create this variable in my Kotlin code:
kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
I have not been able to find away around the following errors:
> Task :sharedLib:linkMainDebugFrameworkIOS FAILED
src/iosMain/kotlin/Platform.kt:9:50: error: unresolved reference: allocPointerTo
val error = kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
^
src/iosMain/kotlin/Platform.kt:9:126: error: no value passed for parameter 'rawPtr'
val error = kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
My understanding is that this package is part of stdlib, which should be added as a dependency automatically. Am I missing a piece of the equation somewhere?
UPDATE
The following is an example of passing an NSError* reference from Kotlin:
memScoped {
val errorRef = alloc<ObjCObjectVar<NSError?>>()
someObjCObject.method(errorRef.ptr)
}

kotlinx.cinterop.NativePlacement is an interface name, not a class name. You need an instance of the NativePlacement interface to work. For example memscoped{..} block will work, e.g.
import kotlinx.cinterop.*
import platform.Foundation.*
val p = memScoped {
allocPointerTo<ObjCObjectVar<NSError?>>()
}
The memscoped{..} accepts a lambda, inside the lambda the receiver implements the NativePlacement type, so there is no need for a qualifier to call a function on it
https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver

Related

Kotlin - Overload resolution ambiguity. All these functions match

I am trying to add a global state store to my topology, but getting error as
Overload resolution ambiguity. All these functions match.
val stateStoreBuilder =
Stores.keyValueStoreBuilder(
Stores.persistentKeyValueStore("my_global_store"),
Serdes.String(),
Serdes.String()
)
topology.addGlobalStore(
stateStoreBuilder,
"source_name",
KEY_JSON_DE,
VALUE_JSON_DE,
"topic_name",
"processor_name",
{ MyStoreProcessor::class.java },
)
Getting error for addGlobalStore method.
Using below versions in Gradle file :-
kotlin("jvm") version "1.7.10"
kotlin("plugin.spring") version "1.7.10"
implementation("org.apache.kafka:kafka-streams:3.3.1")
implementation("org.springframework.kafka:spring-kafka")
I think the problem is shows in the small letter of the error.
The Kotlin compiler cannot figure out which method to use. In particular to what class it should map the last lambda, to org.apache.kafka.streams.processor.ProcessSupplier or org.apache.kafka.streams.processor.api.ProcessSupplier (notice the later has an api package in the middle).
I reckon that if you cast the lambda, it should work. I've never tried this, but I wonder if changing the line to the following would work
{ MyStoreProcessor::class.java } as org.apache.kafka.streams.processor.api.ProcessSupplier

Can't compile Kotlin Jackson Extensions readValue(File)

I am trying to use Kotlin Jackson extensions to do JSON conversions in my code. But for some reason, I am getting a syntax error when trying to use the readValue(File) function.
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3'
---
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
...
private val objectMapper = jacksonObjectMapper()
...
val factionList: List<Faction> = objectMapper.readValue<List<Faction>>(
File(javaClass.classLoader.getResource("data/factions.json").file))
The error I get from the compiler is:
None of the following functions can be called with the arguments supplied.
readValue(JsonParser!, ResolvedType!)
...
[it lists all the valid function signatures ...]
However, none of the extension functions seem to be showing up in that list. If I click on the function and hit Cmd-B in IntelliJ, I am seeing the readValue(File) method in the extensions code.
I am confused why the function is not being found by the compiler.
You're most likely missing the following import:
import com.fasterxml.jackson.module.kotlin.readValue
Forgot this a few times myself.

Annotation class does not validate input as enum in Kotlin

I'd like to create annotation instead of enum use it for when statement
#Retention(AnnotationRetention.SOURCE)
#IntDef(SELECT.toLong(), WEAR.toLong(), BAND.toLong())
annotation class CurrentState
companion object {
const val SELECT = 0
const val WEAR = 1
const val BAND = 2
}
private fun handleFragment(#CurrentState state:Int) {
val fragment:Fragment =
when(state){
SELECT -> SelectDeviceFragment.newInstance()
WEAR -> ConnectWatchFragment.newInstance()
BAND -> SelectDeviceFragment.newInstance()
}
From my understanding, this code should prevent me from performing following code:
handleFragment(5)
and when statement should not ask me to add else statement, as expected from enum
What I'm doing wrong or misunderstand?
From https://discuss.kotlinlang.org/t/intdef-and-stringdef-not-being-checked-at-compile-time/7029:
This checking doesn’t come from the compiler, but from Android lint. Work to make android lint language independent is being done, but if I’m not mistaken you’ll need a newer version of Android Studio for it.
And Android Studio 3.1 blog post mentions lint checks for Kotlin as a feature (though it doesn't say whether this check specifically is supported).
The #IntDef annotation is part of the Android framework. The compiler is not aware of any specific semantics of this annotation, and is not able to use it for checking the exhaustiveness of when statements.
Moreover, even with the annotation, you can call handleFragment(5). Such code will not be a compiler error, it will only be reported as a lint warning.

Get type of a variable in Kotlin

How can I find the variable type in Kotlin?
In Java there is instanceof, but Kotlin does not exist:
val properties = System.getProperties() // Which type?
You can use the is operator to check whether an object is of a specific type:
val number = 5
if(number is Int) {
println("number is of type Int")
}
You can also get the type as String using reflection:
println(Int::class.simpleName) // "Int"
println(Int::class.qualifiedName) // "kotlin.Int"
Please note:
On the Java platform, the runtime component required for using the
reflection features is distributed as a separate JAR file
(kotlin-reflect.jar). This is done to reduce the required size of the
runtime library for applications that do not use reflection features.
If you do use reflection, please make sure that the .jar file is added
to the classpath of your project.
Source: https://kotlinlang.org/docs/reference/reflection.html#bound-class-references-since-11
You can use like this:
val value="value"
println(value::class.java.typeName)
you can get the class name with properties::class.simpleName
Just a minor detail between mentioned answers.
var x = "X"
println(x::class.simpleName) // prints String
This code uses Reflection under the hood when you decompile it to Java bytecode and it looks like this Reflection.getOrCreateKotlinClass(x.getClass()).getSimpleName()
var y = "Y"
println(y.javaClass.simpleName) // prints String
And this would compile down to y.getClass().getSimpleName() and it's about 50 milliseconds faster.

Jinq in Kotlin - how to convert lambda into java SerializedLambda?

Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.