Can I create a class array in D? Something like:
interface A {}
class AA: A {}
class AB: A {}
class AC: A {}
ClassList!A list = new ClassList!A {AA, AB, AC};
void testf(ulong testv) {
A a = new list[testv];
}
Yes, that's possible, though not necessarily exactly how you have it there. You can make a list of types with a type tuple:
import std.typetuple;
alias list = TypeTuple!(AA, AB, AC);
But, you can't index it like an array at runtime; trying new list[n] will be a compile time error. Instead, you'll create a helper function that loops over it and returns the instance, like this:
foreach(index, type; list)
if(index == testv) {
a = new type();
break;
}
The way that compiles is foreach(foo; compile_time_list) actually becomes a big unrolled loop. The generated code is as if you wrote:
if(0 == testv) { a = new AA(); goto end; }
if(1 == testv) { a = new AB(); goto end; }
if(2 == testv) { a = new AC(); goto end; }
end:
So that works with runtime values, bridging the gap between the compile time list and the array index you want.
It's worth noting that this isn't necessarily the most efficient, but unless your class list has like a thousand entries, i doubt that would matter. Another option for more speed is to generate a switch statement at compile time from your list, then mix that in. The switch statement will compile to a more efficient lookup table. But odds are the simple loop is fine.
Anyway, putting it together, we get:
import std.stdio;
interface A {}
class AA: A {}
class AB: A {}
class AC: A {}
import std.typetuple;
alias list = TypeTuple!(AA, AB, AC);
void testf(ulong testv) {
A a;
foreach(index, type; list)
if(index == testv) {
a = new type();
break;
}
if(a is null)
writeln("bad type index");
else {
writeln(typeid(cast(Object) a), " was created");
}
}
void main() {
testf(0);
testf(1);
testf(2);
testf(3);
}
Result:
$ ./test50
test50.AA was created
test50.AB was created
test50.AC was created
bad type index
just what we wanted.
BTW, the typeid(cast(Object) a) might look weird, that's fetching the dynamic class of the type. It has to cast to Object first because otherwise, it would print the interface name. Since D interfaces aren't necessarily D classes (they can also be COM objects or C++ classes), the typeid isn't always available. A cast to Object ensures that it is a D class and thus grabs the run time type details.
EDIT: I saw you asked on the newsgroup as well for checking the base class in the loop. Here's how to do that:
You could write your own tuple template for it, or just let the
compile fail on the factory function: the A a = new T(); will
fail if A isn't a base class or interface of T.
Putting the check in the list could look like this:
bool checkClassList(Base, T...)() {
foreach(t; T) {
static if(!is(t : Base))
static assert(0, t.stringof ~ " is not a child of " ~ Base.stringof);
}
return true;
}
template ClassList(Base, T...) if(checkClassList!(Base, T)) {
alias ClassList = T;
}
Usage:
alias list = ClassList!(A, AA, AB, AC); // good
add:
class B {}
alias list = ClassList!(A, AA, AB, AC, B);
and get error:
test50.d(12): Error: static assert "B is not a child of A"
test50.d(19): instantiated from here: checkClassList!(A, AA, AB, AC, B)
Related
I am trying to learn Kotlin/Native C interop
I exported some Kotlin classes as C dynamic Lib and succeeded in access methods with primitive return types
But When trying to access class members in a instance object which returned by a method, the object contains something named as pinned
Code sample:
#Serializable
data class Persons (
val results: Array<Result>,
val info: Info
)
class RandomUserApiJS {
fun getPersonsDirect() : Persons {
return runBlocking {
RandomUserApi().getPersons()
}
}
}
Now when using them in C codeblocks,
In this image, note that the persons obj only showing a field named pinned and no other member functions found.
Since I don't know that much in C/C++ and can't investigate further.
Please help me to understand to access instance members of Kotlin Class in exported C lib?
Header file for ref:
https://gist.github.com/RageshAntony/a0b9007376084fa8b213b022b58f9886
for your gist
https://gist.github.com/RageshAntony/a0b9007376084fa8b213b022b58f9886
I modified the following:
// I comment this annotation
// #Serializable
data class Persons(
val results: List<Result>,
val info: Info,
/**
* the Result's properties too many
* I will use a simple data class for this example
* how to get c array from Persons (also suitable any iterable)
*/
val testList: List<Simple>,
) {
public fun toJson() = Json.encodeToString(this)
companion object {
public fun fromJson(json: String) = Json.decodeFromString<Persons>(json)
}
val arena = Arena()
fun getTestListForC(size: CPointer<IntVar>): CPointer<COpaquePointerVar> {
size.pointed.value = testList.size
return arena.allocArray<COpaquePointerVar>(testList.size) {
this.value = StableRef.create(testList[it]).asCPointer()
}
}
fun free() {
arena.clear()
}
}
/**
* kotlin <-> c bridge is primitive type
* like int <-> Int
* like char* <-> String
* so the Simple class has two primitive properties
*/
data class Simple(
val name: String,
val age: Int,
)
#include <stdio.h>
#include "libnative_api.h"
int main(int argc, char **argv) {
libnative_ExportedSymbols* lib = libnative_symbols();
libnative_kref_MathNative mn = lib->kotlin.root.MathNative.MathNative();
const char *a = lib->kotlin.root.MathNative.mul(mn,5,6); // working
printf ("Math Resullt %s\n",a);
libnative_kref_RandomUserApiJS pr = lib->kotlin.root.RandomUserApiJS.RandomUserApiJS();
libnative_kref_Persons persons = lib->kotlin.root.RandomUserApiJS.getPersonsDirect(pr);
// when accessing above persons obj, only a field 'pinned' availabe, nothing else
int size;
libnative_kref_Simple* list = (libnative_kref_Simple *)lib->kotlin.root.Persons.getTestListForC(persons, &size);
printf("size = %d\n", size);
for (int i = 0; i < size; ++i) {
const char *name = lib->kotlin.root.Simple.get_name(list[i]);
int age = lib->kotlin.root.Simple.get_age(list[i]);
printf("%s\t%d\n", name, age);
}
lib->kotlin.root.Persons.free(persons);
return 0;
}
// for output
Math Resullt The answer is 30
size = 3
name1 1
name2 2
name3 3
But I don't think calling kotlin lib through C is a good behavior, because kotlin native is not focused on improving performance for now, in my opinion, all functions that can be implemented with kotlin native can find solutions implemented in pure c, So I'm more focused on how to access the c lib from kotlin. Of course, it's a good solution if you absolutely need to access klib from c, but I'm still not very satisfied with it, then I may create a github template to better solve kotlin-interop from c.But that's not the point of this answer.
Abstract problem:
Consider the following:
data class Data(val i: Int, val s: String = "")
fun <A1, A2, D> make(ctor: KFunction2<A1, A2, D>, sideEffect: (D) -> D) =
{ a1: A1, a2: A2 -> sideEffect(ctor(a1, a2)) }
val makeData = make(::Data) {
it.also { println("Side effect: i=${it.i}, s=${it.s}") }
}
I can call makeData with all of the constructor arguments to obtain
an instance of Data and also call some side-effect. However, I
cannot omit parameters and use the constructor's default values instead:
val d1 = makeData(42, "hi")
val d2 = makeData2(42) // Error: No value passed for parameter 'p2'
How can I rewrite the generic make such that its output is a function with default values?
What I'm trying to achieve:
With Kotlin React, I declare properties like so:
external interface VideoListProps: RProps { var videos: List<Video> }
To use the component within render(), I can write:
child(VideoList::class) { attrs.videos = unwatchedVideos }
I don't like this for three reasons: (a) using a component is much more verbose than compared to TypeScript/TSX,
(b) properties need to be declared as var while they should be read-only (val), (c) there is no enforcement that a member is actually declared; props.videos may end up as null at runtime even though the Props interface does not declare it as nullable.
The tutorial for Kotlin React suggests to use lambdas with receivers like so:
fun RBuilder.videoList(handler: VideoListProps.() -> Unit): ReactElement {
return child(VideoList::class) {
this.attrs(handler)
}
}
Now I can use the component like so:
videoList { videos = unwatchedVideos }
While the calling site now looks much more concise, I now have to copy/paste and adapt the lambda snippet for each and every component I write. This is much more verbose than in TypeScript, where I don't need this construct. Moreover, the videoList lambda has no access to other members of the RBuilder as this now points to RProps. So if I need to define a React key, I'm at a loss and need to use the child(...) syntax from before. And lastly, the problem (c) from above still exists.
To make this better, I wrote the following helper constructs:
fun <P : RProps, A1> RBuilder.childWithProps(
klass: KClass<out Component<P, *>>,
ctor: KFunction1<A1, P>
) =
{ a1: A1, handler: RHandler<P> -> child(klass.rClass, ctor(a1), handler) }
fun <P : RProps, A1, A2> RBuilder.childWithProps(
klass: KClass<out Component<P, *>>,
ctor: KFunction2<A1, A2, P>
) =
{ a1: A1, a2: A2, handler: RHandler<P> -> child(klass.rClass, ctor(a1, a2), handler) }
// more for KFunction3, KFunction4, ... up to a reasonable amount of members, e.g. 16
I admit this is also kind of ugly, but I need to write this once only. Now I can declare props like this (note the val instead of var):
data class VideoListProps(val videos: List<Videos>) : RProps
The additional code for each component is now much more concise:
val RBuilder.videoList get() = childWithProps(VideoList::class, ::VideoListProps)
And use it like this:
videoList(watchedVideos) { /* you can set React key or children here if needed */ }
Kotlin now marks it as an error if I forget to set all properties.
Everything now looks concise, I write even slightly less code than in TypeScript/TSX. The only remaining problem is that now I always have to pass all members, since any default values of the data constructor will still be madatory in the wrapper. This is the actual question.
I'm wondering about methods of mapping multiple arrays into one list of object.
I mean e.g. I have
val a = arrayOf("A1","A2","A3")
val b = arrayOf("B1","B2","B3")
and
data class SomeClass(val v1:String, val v2:String)
I want to parse it in elegant way to have list like that:
val list = listOf(SomeClass("A1","B1"),SomeClass("A2","B2"),SomeClass("A3","B3"))
I assume they are of the same length. The only way I thought of is:
val list = mutableListOf<SomeClass>()
for (i in a.indices)
array.add(SomeClass(a[i],b[i])
Is there a better, more elegant solution (maybe using Collecions.zip or Array.map)?
Try Array.zip and then map:
val list = a.zip(b)
.map { SomeClass(it.first, it.second) }
or if you like it more:
val list = a.zip(b)
.map { (a, b) -> SomeClass(a, b) }
Note that if both arrays differ in size, the additional values are ignored. Note also that this will create intermediate Pairs (which is the default transformation function of zip). Even though I like the explicit map more, #hotkeys solution regarding the overloaded method is more appropriate (you spare that hidden Pair-transformation):
val list = a.zip(b) { a, b -> SomeClass(a, b) }
And where the overloaded method probably shines, is when using references instead:
a.zip(b, ::SomeClass)
Which will work as long as you have a constructor matching the zipped arguments and doesn't work out of the box for the Pair (yet?).
Improving on #Roland's answer, you can use the zip overload that accepts a two-argument function for mapping the pairs immediately:
val result = a.zip(b) { x, y -> SomeClass(x, y) }
You can write some custom fun like this:
inline fun <T, R, E, V> Iterable<T>.zipThree(other1: Iterable<R>, other2: Iterable<E>, transform: (T, R, E) -> V): List<V> {
val first = iterator()
val second = other1.iterator()
val third = other2.iterator()
val list = ArrayList<V>()
while (first.hasNext() && second.hasNext()) {
list.add(transform(first.next(), second.next(), third.next()))
}
return list
}
And use this transform for getting List
val strings = listOf("1", "2")
val ints = listOf(1, 2)
val boolean = listOf(true, false)
val listYoutObjects = strings.zipThree(ints, boolean) { one, two, three -> YouObject(one, two, three) }
I'm writing a method to output to several output streams at once, the way I got it set up right now is that I have a LogController, LogFile and LogConsole, the latter two are implementations of the Log interface.
What I'm trying to do right now adding a method to the LogController that attaches any implementation of the Log interface.
How I want to do this is as follows: in the LogController I have an associative array, in which I store pointers to Log objects. When the writeOut method of the LogController is called, I want it to then run over the elements of the array and call their writeOut methods too. The latter I can do, but the previous is proving to be difficult.
Mage/Utility/LogController.d
module Mage.Utility.LogController;
import std.stdio;
interface Log {
public void writeOut(string s);
}
class LogController {
private Log*[string] m_Logs;
public this() {
}
public void attach(string name, ref Log l) {
foreach (string key; m_Logs.keys) {
if (name is key) return;
}
m_Logs[name] = &l;
}
public void writeOut(string s) {
foreach (Log* log; m_Logs) {
log.writeOut(s);
}
}
}
Mage/Utility/LogFile.d
module Mage.Utility.LogFile;
import std.stdio;
import std.datetime;
import Mage.Utility.LogController;
class LogFile : Log {
private File fp;
private string path;
public this(string path) {
this.fp = File(path, "a+");
this.path = path;
}
public void writeOut(string s) {
this.fp.writefln("[%s] %s", this.timestamp(), s);
}
private string timestamp() {
return Clock.currTime().toISOExtString();
}
}
I've already tried multiple things with the attach functions, and none of them. The build fails with the following error:
Mage\Root.d(0,0): Error: function Mage.Utility.LogController.LogController.attach (string name, ref Log l) is not callable using argument types (string, LogFile)
This is the incriminating function:
public void initialise(string logfile = DEFAULT_LOG_FILENAME) {
m_Log = new LogController();
LogFile lf = new LogFile(logfile);
m_Log.attach("Log File", lf);
}
Can anyone tell me where I'm going wrong here? I'm stumped and I haven't been able to find the answer anywhere. I've tried a multitude of different solutions and none of them work.
Classes and interfaces in D are reference types, so Log* is redundant - remove the *. Similarly, there is no need to use ref in ref Log l - that's like taking a pointer by reference in C++.
This is the cause of the error message you posted - variables passed by reference must match in type exactly. Removing the ref should solve the error.
I just had a problem in one of my projects. Maybe I got the wrong concept about encapsulation.
Encapsulation protects member variables from classes, by defining getters and setters methods, now, i was reading that setters must be void, but in that case, how can I know if the function really set the value passed by argument. For example
void setArea(int a) {
if(a>0)
Area = a;
}
How can I be sure that argument "a" was a correct value, wouldnt be better defining the function like this
bool setArea(int a) {
if(a>0) {
Area = a;
return true;
}
return false;
}
Is that ok? that way i can know if a change really happened.
I think what you're looking for is a guard clause that throws an exception if invalid values are set:
void setArea(int a) {
if (a <= 0) throw new InvalidArgumentException(...);
Area = a;
}
But if you want client code to test for invalid values before setting them, you could have this:
bool isAreaValid(int a) {
return a > 0;
}
void setArea(int a) {
if (!isAreaValid(a)) throw new InvalidArgumentException(...);
Area = a;
}
Clients could be coded like this:
if (obj.isAreaValid(myArea)) {
obj.setArea(myArea);
}
But don't stop there. If the concept of area is important, spring it into existence into its own value object to make your design clearer.