cleanup and fixes for libnice support
This commit is contained in:
parent
2995abfd57
commit
8966bd9ea1
13 changed files with 1252 additions and 449 deletions
52
README.adoc
52
README.adoc
|
|
@ -1,6 +1,6 @@
|
|||
= High level GTK3 (and GTK4) bindings for the Nim programming language
|
||||
(c) Stefan Salewski
|
||||
//Version 0.8.2
|
||||
//Version 0.8.3
|
||||
:experimental:
|
||||
:imagesdir: http://ssalewski.de/tmp
|
||||
:source-highlighter: pygments
|
||||
|
|
@ -25,6 +25,56 @@ with code inserted from files directly.
|
|||
NOTE: This work is partly based on earlier works of J. Mansour and has been supported by A. Rumpf, E. Bassi and other _Nim_ and _GTK/Gnome_ developers.
|
||||
The `combinatorics` module was kindly provided by R. Behrends.
|
||||
|
||||
NOTE: We are currently preparing v0.8.3. You may install it with command [black yellow-background]#nimble install gintro@#head#.
|
||||
This version will have some internal cleanup, which removes the temporary
|
||||
Array types. In the past we used that names to indicate array parameters, it was working well, but still
|
||||
it was ugly using names to indicates data types. So we fixed that. And we have added some better support for
|
||||
GList parameter. GLists are now converted to Nim seqs and vice versa. Not everywhere still, and this conversion
|
||||
is still untested, as GList parameters are used most of the time in exotic functions only. Another change is that
|
||||
we have support for libnice by a request of a user from Japan. We added the sdp_example.nim converted from
|
||||
a C example, it seems to compile and run. And finally with v0.8.3 the examples from the GTK4 book should work.
|
||||
For the GTK3 examples we had to do some minimal fixes, so you may have to do similar fixes for your own code as well.
|
||||
For gintro/examples/gtk3 this was
|
||||
|
||||
----
|
||||
# examples modifications for v0.8.3:
|
||||
$ diff gintronew/examples/gtk3 gintro/examples/gtk3
|
||||
diff gintronew/examples/gtk3/celldatafunction.nim gintro/examples/gtk3/celldatafunction.nim
|
||||
3c3
|
||||
< import gintro/[gtk, gobject, glib]
|
||||
---
|
||||
> import gintro/[gtk, gobject]
|
||||
67c67
|
||||
< let store = newListStore(Columns, cast[ptr GType](unsafeaddr gtype)) # ugly cast
|
||||
---
|
||||
> let store = newListStore(Columns, cast[pointer](unsafeaddr gtype)) # ugly cast
|
||||
diff gintronew/examples/gtk3/css_colored_listview.nim gintro/examples/gtk3/css_colored_listview.nim
|
||||
64c64
|
||||
< var store = newListStore(Columns, cast[ptr GType]( unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
---
|
||||
> var store = newListStore(Columns, cast[pointer]( unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
diff gintronew/examples/gtk3/gearsmenu.nim gintro/examples/gtk3/gearsmenu.nim
|
||||
114c114
|
||||
< echo fmt"GTK Version {getMajorVersion()}.{getMinorVersion()}.{getMicroVersion()}"
|
||||
---
|
||||
> echo fmt"GTK Version {majorVersion()}.{minorVersion()}.{microVersion()}"
|
||||
diff gintronew/examples/gtk3/listview.nim gintro/examples/gtk3/listview.nim
|
||||
61c61
|
||||
< let store = newListStore(N_COLUMNS, cast[ptr GType]( unsafeaddr gtype)) # cast due to bug in gtk.nim
|
||||
---
|
||||
> let store = newListStore(N_COLUMNS, cast[pointer]( unsafeaddr gtype)) # cast due to bug in gtk.nim
|
||||
diff gintronew/examples/gtk3/menubar.nim gintro/examples/gtk3/menubar.nim
|
||||
101c101
|
||||
< echo "GTK Version $1.$2.$3" % [$getMajorVersion(), $getMinorVersion(), $getMicroVersion()]
|
||||
---
|
||||
> echo "GTK Version $1.$2.$3" % [$majorVersion(), $minorVersion(), $microVersion()]
|
||||
diff gintronew/examples/gtk3/overlay_tree1.nim gintro/examples/gtk3/overlay_tree1.nim
|
||||
71c71
|
||||
< let store = newListStore(Columns, cast[ptr GType](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
---
|
||||
> let store = newListStore(Columns, cast[pointer](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
----
|
||||
|
||||
NOTE: This is version 0.8.2 of gintro. It is only a fix for recent gstreamer 1.18 which added some uncommon
|
||||
gobject-introspection stuff which brook the install of 0.8.1 for a few users. We where not yet able to
|
||||
verify that the gst example which is bases on the gst C tutorial1 works with gstreamer 1.18.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# This example shows how to apply a CellDataFunc to a GtkTreeView
|
||||
# C example code was provided by A.Krause in chapter 8 of his book
|
||||
import gintro/[gtk, gobject]
|
||||
import gintro/[gtk, gobject, glib]
|
||||
|
||||
const
|
||||
Color = 0
|
||||
|
|
@ -64,7 +64,7 @@ proc main =
|
|||
let treeview = newTreeView()
|
||||
setupTreeView(treeview)
|
||||
let gtype = typeFromName("gchararray")
|
||||
let store = newListStore(Columns, cast[pointer](unsafeaddr gtype)) # ugly cast
|
||||
let store = newListStore(Columns, cast[ptr GType](unsafeaddr gtype)) # ugly cast
|
||||
## Add all of the products to the GtkListStore.
|
||||
for i in 0 ..< 6:
|
||||
for j in 0 ..< 6:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ proc main() =
|
|||
var iter: TreeIter
|
||||
var h = [typeFromName("guint"), typeFromName("gchararray"), typeFromName("gchararray"),
|
||||
typeFromName("gchararray")]
|
||||
var store = newListStore(Columns, cast[pointer]( unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
var store = newListStore(Columns, cast[ptr GType]( unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
let progNames = ["Gedit", "Gimp", "Inkscape", "Firefox", "Calculator", "Devhelp"]
|
||||
for i, n in progNames:
|
||||
store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ proc main =
|
|||
let app = newApplication("app.example")
|
||||
connect(app, "startup", appStartup)
|
||||
connect(app, "activate", appActivate)
|
||||
echo fmt"GTK Version {majorVersion()}.{minorVersion()}.{microVersion()}"
|
||||
echo fmt"GTK Version {getMajorVersion()}.{getMinorVersion()}.{getMicroVersion()}"
|
||||
let status = run(app)
|
||||
quit(status)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ proc initList(list: TreeView) =
|
|||
column.addAttribute(renderer, "text", LIST_ITEM)
|
||||
discard list.appendColumn(column)
|
||||
let gtype = gStringGetType() # typeFromName("gchararray")
|
||||
let store = newListStore(N_COLUMNS, cast[pointer]( unsafeaddr gtype)) # cast due to bug in gtk.nim
|
||||
let store = newListStore(N_COLUMNS, cast[ptr GType]( unsafeaddr gtype)) # cast due to bug in gtk.nim
|
||||
list.setModel(store)
|
||||
|
||||
proc appActivate(app: Application) =
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ proc main =
|
|||
let app = newApplication("app.example")
|
||||
connect(app, "startup", appStartup)
|
||||
connect(app, "activate", appActivate)
|
||||
echo "GTK Version $1.$2.$3" % [$majorVersion(), $minorVersion(), $microVersion()]
|
||||
echo "GTK Version $1.$2.$3" % [$getMajorVersion(), $getMinorVersion(), $getMicroVersion()]
|
||||
let status = run(app)
|
||||
quit(status)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ proc main =
|
|||
var iter: TreeIter
|
||||
let h = [typeFromName("guint"), typeFromName("gchararray"), typeFromName("gchararray"),
|
||||
typeFromName("gchararray")]
|
||||
let store = newListStore(Columns, cast[pointer](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
let store = newListStore(Columns, cast[ptr GType](unsafeaddr h)) # cast is ugly, we should fix it in bindings.
|
||||
let progNames = ["Gedit", "Gimp", "Inkscape", "Firefox", "Calculator", "Devhelp"]
|
||||
for i, n in progNames:
|
||||
store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original
|
||||
|
|
|
|||
219
examples/gtk3/sdp_example.nim
Normal file
219
examples/gtk3/sdp_example.nim
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
# https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/examples/sdp-example.c
|
||||
# nim c --gc:arc sdp_example.nim
|
||||
#
|
||||
# Example using libnice to negotiate a UDP connection between two clients,
|
||||
# possibly on the same network or behind different NATs and/or stateful
|
||||
# firewalls.
|
||||
#
|
||||
# Run two clients, one controlling and one controlled:
|
||||
# ./sdp-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||
# ./sdp-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||
|
||||
import gintro/[gtk, glib, gobject, nice]
|
||||
import gintro/gio except stdin
|
||||
from strutils import `%`
|
||||
from os import paramCount, paramStr
|
||||
from strutils import parseInt
|
||||
|
||||
var
|
||||
gloop: glib.MainLoop
|
||||
stunAddr: string
|
||||
stunPort: int
|
||||
controlling: int
|
||||
exitThread: bool
|
||||
candidateGatheringDone: bool
|
||||
negotiationDone: bool
|
||||
gatherMutex: glib.Mutex
|
||||
negotiateMutex: glib.Mutex
|
||||
gatherCond: glib.Cond
|
||||
negotiateCond: glib.Cond
|
||||
stateName = ["disconnected", "gathering", "connecting", "connected", "ready", "failed"]
|
||||
|
||||
proc gDebug(s: string) =
|
||||
stdout.write(s)
|
||||
flushFile(stdout)
|
||||
|
||||
proc gError(s: string) =
|
||||
stderr.write(s)
|
||||
flushFile(stderr)
|
||||
|
||||
proc toStringVal(s: string): Value =
|
||||
let gtype = gStringGetType()
|
||||
discard init(result, gtype)
|
||||
setString(result, s)
|
||||
|
||||
proc toBoolVal(b: bool): Value =
|
||||
let gtype = gBooleanGetType()
|
||||
discard init(result, gtype)
|
||||
setBoolean(result, b)
|
||||
|
||||
proc toIntVal(i: int): Value =
|
||||
let gtype = typeFromName("gint")
|
||||
discard init(result, gtype)
|
||||
setInt(result, i)
|
||||
|
||||
proc cbCandidateGatheringDone(agent: nice.Agent; streamID: int) =
|
||||
gDebug("SIGNAL candidate gathering done\n")
|
||||
glib.lock(gatherMutex)
|
||||
candidateGatheringDone = true
|
||||
glib.signal(gatherCond)
|
||||
glib.unlock(gatherMutex)
|
||||
|
||||
proc cbComponentStateChanged(agent: nice.Agent; streamID: int; componentID: int; state: int) =
|
||||
gDebug("SIGNAL: state changed $1 $2 $3[$4]\n" % [$streamID, $componentID, stateName[state.ord], $state])
|
||||
if state == nice.ComponentState.ready.ord:
|
||||
glib.lock(negotiateMutex)
|
||||
negotiationDone = true
|
||||
glib.signal(negotiateCond)
|
||||
glib.unlock(negotiateMutex)
|
||||
elif state == nice.ComponentState.failed.ord:
|
||||
glib.quit(gloop)
|
||||
|
||||
# proc nice_agent_attach_recv*(self: ptr Agent00; streamID: cuint; componentID: cuint; ctx: ptr glib.MainContext;
|
||||
# fn: AgentRecvFunc; data: pointer): gboolean {.importc, libprag.}
|
||||
#
|
||||
# this has to be a low level function!
|
||||
# AgentRecvFunc* = proc (agent: ptr Agent00; streamId: uint32; componentId: uint32; len: uint32;
|
||||
# buf: cstring; userData: pointer) {.cdecl.}
|
||||
proc cbNiceRecv(agent: ptr nice.Agent00; streamID: uint32; componentID: uint32; len: uint32; buf: cstring; userData: pointer) {.cdecl.} =
|
||||
if len == 1 and buf[0] == '\x00':
|
||||
glib.quit(gloop)
|
||||
discard stdout.writeBuffer(buf, len)
|
||||
flushfile(stdout)
|
||||
|
||||
proc exampleThread(data: pointer): pointer {.cdecl.} =
|
||||
var
|
||||
agent: nice.Agent
|
||||
ioStdin: glib.IOChannel
|
||||
streamID: int
|
||||
line: string
|
||||
sdp: string
|
||||
sdp64: string
|
||||
#when defined(G_OS_WIN32):
|
||||
# io_stdin = g_io_channel_win32_new_fd(_fileno(stdin))
|
||||
#else:
|
||||
ioStdin = unixNew(getFileHandle(stdin))
|
||||
discard setFlags(ioStdin, glib.IOFlags.nonBlock)
|
||||
# Create the nice agent
|
||||
agent = nice.newAgent(glib.getContext(gloop), nice.CompatibilityRfc5245)
|
||||
if agent == nil:
|
||||
gError("Failed to create agent")
|
||||
# Set the STUN settings and controlling mode
|
||||
if stunAddr.len > 0:
|
||||
setProperty(agent, "stun-server", toStringVal(stunAddr))
|
||||
setProperty(agent, "stun-server-port", toIntVal(stunPort))
|
||||
setProperty(agent, "controlling-mode", toIntVal(controlling))
|
||||
# Connect to the signals
|
||||
agent.connect("candidate-gathering-done", cbCandidateGatheringDone)
|
||||
agent.connect("component-state-changed", cbComponentStateChanged)
|
||||
# Create a new stream with one component
|
||||
streamID = addStream(agent, 1)
|
||||
if streamID == 0:
|
||||
gError("Failed to add stream")
|
||||
discard setStreamName(agent, streamID, "text")
|
||||
# Attach to the component to receive the data
|
||||
# Without this call, candidates cannot be gathered
|
||||
discard attachRecv(agent, streamID, 1, getContext(gloop), cbNiceRecv, nil)
|
||||
# Start gathering local candidates
|
||||
if not gatherCandidates(agent, stream_id):
|
||||
gError("Failed to start candidate gathering")
|
||||
gDebug("waiting for candidate-gathering-done signal...")
|
||||
lock(gatherMutex)
|
||||
while not exitThread and not candidateGatheringDone:
|
||||
wait(gatherCond, gatherMutex)
|
||||
unlock(gatherMutex)
|
||||
block gotoEnd:
|
||||
if exitThread:
|
||||
break gotoEnd
|
||||
# Candidate gathering is done. Send our local candidates on stdout
|
||||
sdp = generateLocalSdp(agent)
|
||||
stdout.write("Generated SDP from agent :\n$1\n\n" % [sdp])
|
||||
stdout.write("Copy the following line to remote client:\n")
|
||||
sdp64 = base64Encode(sdp)
|
||||
stdout.write("\n $1\n", sdp64)
|
||||
# Listen on stdin for the remote candidate list
|
||||
stdout.write("Enter remote data (single line, no wrapping):\n")
|
||||
stdout.write("> ")
|
||||
flushFile(stdout)
|
||||
while not exitThread:
|
||||
var terminatorPos: uint64
|
||||
var length: uint64
|
||||
var s: IOStatus = readLine(ioStdin, line, length, terminatorPos)
|
||||
if s == IOStatus.normal:
|
||||
var sdpLen: uint64
|
||||
let sdpxxx = base64Decode(line, sdpLen) #seq[uint8]
|
||||
sdp = cast[string](sdpxxx)
|
||||
# Parse remote candidate list and set it on the agent
|
||||
if sdp.len > 0 and parseRemoteSdp(agent, sdp) > 0:
|
||||
break
|
||||
else:
|
||||
write(stderr, "ERROR: failed to parse remote data\n")
|
||||
write(stdout, "Enter remote data (single line, no wrapping):\n")
|
||||
stdout.write("> ")
|
||||
flushFile(stdout)
|
||||
elif s == IOStatus.again:
|
||||
usleep(100000)
|
||||
gDebug("waiting for state READY or FAILED signal...")
|
||||
lock(negotiateMutex)
|
||||
while not exitThread and not negotiationDone:
|
||||
wait(negotiateCond, negotiateMutex)
|
||||
unlock(negotiateMutex)
|
||||
if exitThread:
|
||||
break gotoEnd
|
||||
# Listen to stdin and send data written to it
|
||||
stdout.write("\nSend lines to remote (Ctrl-D to quit):\n")
|
||||
stdout.write("> ")
|
||||
flushFile(stdout)
|
||||
while not exitThread:
|
||||
var terminatorPos: uint64
|
||||
var length: uint64
|
||||
var s: IOStatus = readLine(ioStdin, line, length, terminatorPos);
|
||||
if s == IOStatus.normal:
|
||||
discard send(agent, streamID, 1, line.len, line)
|
||||
stdout.write("> ")
|
||||
flushFile(stdout)
|
||||
elif s == IOStatus.again:
|
||||
usleep(100000)
|
||||
else:
|
||||
# Ctrl-D was pressed.
|
||||
discard send(agent, stream_id, 1, 1, "\x00")
|
||||
break
|
||||
# END label
|
||||
quit(gloop)
|
||||
return nil
|
||||
|
||||
proc main =
|
||||
echo paramCount()
|
||||
for i in 0 .. paramCount():
|
||||
echo paramStr(i)
|
||||
let argc = paramCount() + 1
|
||||
var argv = newSeq[string](argc)
|
||||
for i in 0 ..< argc:
|
||||
argv[i] = paramStr(i)
|
||||
var gexamplethread: glib.Thread
|
||||
# Parse arguments
|
||||
if argc > 4 or argc < 2 or argv[1].len > 1: # argv[1][1] != '\x00':
|
||||
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
|
||||
quit(QuitFailure)
|
||||
controlling = argv[1][0].ord - '0'.ord
|
||||
if controlling != 0 and controlling != 1:
|
||||
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
|
||||
quit(QuitFailure)
|
||||
if argc > 2:
|
||||
stun_addr = argv[2]
|
||||
if argc > 3:
|
||||
stun_port = parseInt(argv[3])
|
||||
else:
|
||||
stun_port = 3478
|
||||
gDebug("Using stun server \'[$1]:$2\'\n" % [$stunAddr, $stunPort])
|
||||
gio.networkingInit()
|
||||
gloop = newMainLoop(nil, false)
|
||||
# Run the mainloop and the example thread
|
||||
exitThread = false
|
||||
gexamplethread = newThread("example thread", exampleThread, nil)
|
||||
run(gloop)
|
||||
exitThread = true
|
||||
discard join(gexamplethread)
|
||||
quit(QuitSuccess)
|
||||
|
||||
main()
|
||||
|
|
@ -117,7 +117,7 @@ proc main =
|
|||
content.setVexpand
|
||||
box.append(content)
|
||||
let footer = newActionBar()
|
||||
footer.setCenterWidget(newCheckButtonWithLabel("Middle"))
|
||||
footer.setCenterWidget(newCheckButton("Middle"))
|
||||
let button = newToggleButtonWithLabel("Custom")
|
||||
button.connect("clicked", changeHeader, window)
|
||||
footer.packStart(button)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
author = "Stefan Salewski"
|
||||
description = "High level GObject-Introspection based GTK3/GTK4 bindings"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from sequtils import keepItIf
|
||||
#from sequtils import keepItIf
|
||||
import sequtils
|
||||
const RecSep = "!" # Record separator for entries in gisup.nim, also defined in gen.nim
|
||||
|
||||
# caution: $$ due to string interpolation
|
||||
|
|
@ -188,6 +189,33 @@ proc $1$2 {.cdecl.} =
|
|||
|
||||
r1s.add(resu & "\n")
|
||||
all = all.replace(")", "; user_data: pointer)")
|
||||
|
||||
#echo all
|
||||
|
||||
if all.find("00Array") >= 0:
|
||||
var h = all.split(';')
|
||||
for el in mitems(h):
|
||||
var a, b: string
|
||||
(a, b) = el.split(": ")
|
||||
if b.find("Array") >= 0:
|
||||
b = "ptr " & b.replace("Array")
|
||||
el = a & ": " & b
|
||||
all = h.join(";")
|
||||
|
||||
#echo all
|
||||
|
||||
# https://forum.nim-lang.org/t/6775#42155
|
||||
const matcher = "Array" # is this good enough?
|
||||
var ra: string
|
||||
for (elem, isSep) in all.tokenize(Whitespace + {',', ';'}):
|
||||
if elem.endsWith(matcher):
|
||||
ra.add("ptr " & elem[0 .. ^(matcher.len + 1)])
|
||||
else:
|
||||
ra.add(elem)
|
||||
|
||||
#echo ra
|
||||
#assert ra == all
|
||||
|
||||
r1s = r1s % [$procNameCdecl, all, $p, wts, ats]
|
||||
#echo r1s
|
||||
|
||||
|
|
@ -449,4 +477,4 @@ $1(column, renderer, $6)
|
|||
""" % [$procName, ats, $column, $renderer, $procNameCdecl, argStr, $procNameDestroy]
|
||||
|
||||
result = parseStmt(r0s & r1s & r2s)
|
||||
# 429 lines
|
||||
# 429 lines echo
|
||||
|
|
|
|||
1088
tests/gen.nim
1088
tests/gen.nim
File diff suppressed because it is too large
Load diff
292
tests/maxby.nim
Normal file
292
tests/maxby.nim
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
# https://forum.nim-lang.org/t/4763
|
||||
|
||||
proc maxIndexBy*[T](s: openArray[T], cmp: proc (x, y: T): int {.closure.}): int =
|
||||
## Returns the index of the maximum value of all elements in s.
|
||||
## Comparison is done by cmp() proc.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxIndexBy(proc (x,y: string): int = cmp(x.len, y.len)) == 1
|
||||
if s.len > 0:
|
||||
var maxVal = s[0]
|
||||
for i in 1 .. s.high:
|
||||
if cmp(s[i], maxVal) > 0:
|
||||
result = i
|
||||
maxVal = s[i]
|
||||
|
||||
proc minIndexBy*[T](s: openArray[T], cmp: proc (x, y: T): int {.closure.}): int =
|
||||
## Returns the index of the minimum value of all elements in s.
|
||||
## Comparison is done by cmp() proc.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minIndexBy(proc (x,y: string): int = cmp(x.len, y.len)) == 3
|
||||
if s.len > 0:
|
||||
var minVal = s[0]
|
||||
for i in 1 .. s.high:
|
||||
if cmp(s[i], minVal) < 0:
|
||||
result = i
|
||||
minVal = s[i]
|
||||
|
||||
# see system.sortedByIt()
|
||||
template maxIndexByIt*(seq1, op: untyped): untyped =
|
||||
## Returns the maximum index of all elements in s.
|
||||
## Comparison is done by it expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxIndexByIt(it.len) == 1
|
||||
var result = maxIndexBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template minIndexByIt*(seq1, op: untyped): untyped =
|
||||
## Returns the minimum index of all elements in s.
|
||||
## Comparison is done by it expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minIndexByIt(it.len) == 3
|
||||
var result = minIndexBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template maxIndex*(seq1, el, op: untyped): untyped =
|
||||
## Returns the maximum index of all elements in s.
|
||||
## Comparison is done by custom expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxIndex(el, el.len) == 1
|
||||
var result = maxIndexBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var el {.inject.} = x
|
||||
let a = op
|
||||
el = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template minIndex*(seq1, el, op: untyped): untyped =
|
||||
## Returns the minimum index of all elements in s.
|
||||
## Comparison is done by custom expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minIndex(el, el.len) == 3
|
||||
var result = minIndexBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var el {.inject.} = x
|
||||
let a = op
|
||||
el = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
func maxValueBy*[T](s: openArray[T], cmp: proc (x, y: T): int {.closure.}): T =
|
||||
## Returns the maximum value of all elements in s.
|
||||
## Comparison is done by cmp() proc.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == "Nim"
|
||||
assert(s.len > 0)
|
||||
result = s[0]
|
||||
if s.len > 0:
|
||||
for i in 1 .. s.high:
|
||||
if cmp(s[i], result) > 0:
|
||||
result = s[i]
|
||||
|
||||
func minValueBy*[T](s: openArray[T], cmp: proc (x, y: T): int {.closure.}): T =
|
||||
## Returns the minimum value of all elements in s.
|
||||
## Comparison is done by cmp() proc.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == "D"
|
||||
assert(s.len > 0)
|
||||
result = s[0]
|
||||
if s.len > 0:
|
||||
for i in 1 .. s.high:
|
||||
if cmp(s[i], result) < 0:
|
||||
result = s[i]
|
||||
|
||||
template maxValueByIt*(seq1, op: untyped): untyped =
|
||||
## Returns the maximum value of all elements in s.
|
||||
## Comparison is done by it expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxValueByIt(it.len) == "Nim"
|
||||
var result = maxValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template minValueByIt*(seq1, op: untyped): untyped =
|
||||
## Returns the minimum value of all elements in s.
|
||||
## Comparison is done by it expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minValueByIt(it.len) == "D"
|
||||
var result = minValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template maxValue*(seq1, el, op: untyped): untyped =
|
||||
## Returns the maximum value of all elements in s.
|
||||
## Comparison is done by custom expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].maxValue(el, el.len) == "Nim"
|
||||
var result = maxValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var el {.inject.} = x
|
||||
let a = op
|
||||
el = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template minValue*(seq1, el, op: untyped): untyped =
|
||||
## Returns the minimum value of all elements in s.
|
||||
## Comparison is done by custom expression
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minValue(el, el.len) == "D"
|
||||
var result = minValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var el {.inject.} = x
|
||||
let a = op
|
||||
el = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
func minMaxValueBy*[T](s: openArray[T], cmp: proc (x, y: T): int {.closure.}): (T, T) =
|
||||
## Returns the minimum and maximum values of all elements in s.
|
||||
## Comparison is done by cmp() proc.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minMaxValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == ("D", "Nim")
|
||||
assert(s.len > 0)
|
||||
if s.len > 0:
|
||||
result[0] = s[0]
|
||||
result[1] = s[0]
|
||||
for i in 1 .. s.high:
|
||||
if cmp(result[1], s[i]) < 0:
|
||||
result[1] = s[i]
|
||||
elif cmp(result[0], s[i]) > 0:
|
||||
result[0] = s[i]
|
||||
|
||||
template minMaxValueByIt*(seq1, op: untyped): untyped =
|
||||
## Returns the minimum and maximum values of all elements in s.
|
||||
## Comparison is done by it expression.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minMaxValueByIt(it.len) == ("D", "Nim")
|
||||
var result = minMaxValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var it {.inject.} = x
|
||||
let a = op
|
||||
it = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
template minMaxValue*(seq1, el, op: untyped): untyped =
|
||||
## Returns the minimum and maximum values of all elements in s.
|
||||
## Comparison is done by custom expression.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:
|
||||
## assert ["Go", "Nim", "C++", "D"].minMaxValue(el, el.len) == ("D", "Nim")
|
||||
var result = minMaxValueBy(seq1, proc(x, y: type(seq1[0])): int =
|
||||
var el {.inject.} = x
|
||||
let a = op
|
||||
el = y
|
||||
let b = op
|
||||
result = cmp(a, b))
|
||||
result
|
||||
|
||||
when isMainModule:
|
||||
|
||||
type
|
||||
Person = object
|
||||
name: string
|
||||
age: int
|
||||
|
||||
var people: seq[Person] = @[Person(name: "Eve", age: 11), Person(name: "Sarah", age: 25), Person(name: "Anna", age: 23)]
|
||||
|
||||
var (min, max) = people.minMaxValue(p, p.name)
|
||||
|
||||
echo "Min: ", min.name, ", ", min.age
|
||||
echo "Max: ", max.name, ", ", max.age
|
||||
|
||||
var m = people.maxValueByIt(it.name.len)
|
||||
|
||||
echo "Max by length of name: ", m.name
|
||||
|
||||
var h = people.minIndexBy(proc (x,y: Person): int = cmp(x.name.len, y.name.len))
|
||||
|
||||
echo "Min index by length of name: ", h
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxIndexBy(proc (x,y: string): int = cmp(x.len, y.len)) == 1
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minIndexBy(proc (x,y: string): int = cmp(x.len, y.len)) == 3
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxIndexByIt(it.len) == 1
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minIndexByIt(it.len) == 3
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxIndex(el, el.len) == 1
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minIndex(el, el.len) == 3
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == "Nim"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == "D"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxValueByIt(it.len) == "Nim"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minValueByIt(it.len) == "D"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].maxValue(el, el.len) == "Nim"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minValue(el, el.len) == "D"
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minMaxValueBy(proc (x,y: string): int = cmp(x.len, y.len)) == ("D", "Nim")
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minMaxValueByIt(it.len) == ("D", "Nim")
|
||||
|
||||
assert ["Go", "Nim", "C++", "D"].minMaxValue(el, el.len) == ("D", "Nim")
|
||||
Loading…
Add table
Add a link
Reference in a new issue