Add a map element on access the map with new key

Just starting out? Need help? Post your questions and find answers here.
danilocoelho
New User
New User
Posts: 9
Joined: Sun Mar 04, 2018 3:39 pm
Location: Cianorte, PR - Brazil

Add a map element on access the map with new key

Post by danilocoelho »

This example of code is based in help AddMapElement().
I'm use PureBasic 5.62 (Linux - x64).

Please see my question in the code comments.

Code: Select all

Structure Country_
  name.s
  continent.s
EndStructure

NewMap Country.Country_()

; Regular way to add an element
With Country("US")
  \name = "United States"
  \continent = "America"
EndWith

Debug "Map Element US = " + @Country()

; The same using AddMapElement()
Define *france.Country_ = AddMapElement(Country(), "FR")
With *france
  \name = "France"
  \continent = "Europe"
EndWith

Debug "Map Element *france = " + *france

; Not documented way, but similar the regular way.
; This key don't exists then new map element is created and pointer returned as expected! Exactly as AddMapElement...
Define *brazil.Country_ = Country("BR")
*brazil\name = "Brazil"
*brazil\continent = "America"

Debug "Map Element *brazil = " + *brazil

; But new map element isn't accessible 
ForEach Country()
  Debug Country()\name + " - " + Country()\continent + " | Pointer = " + @Country()
Next

Debug "FindMapElement BR = " + FindMapElement(Country(), "BR")
Debug "MapSize = " + MapSize(Country())

Even considering this part of the documentation:
Remarks
     This function is not mandatory when dealing with maps, the elements are automatically added when affecting them.
I think accessing the map with a new key should implicitly invoke AddMapElement or return #Null as it does in FindMapElement.

What do you think?

Att,
Danilo S. Coelho.
Micoute
User
User
Posts: 28
Joined: Sat Jun 22, 2013 4:06 pm
Location: La Mézière FRANCE

Re: Add a map element on access the map with new key

Post by Micoute »

Define *brazil.Country_ = AddMapElement(Country(),"BR")
danilocoelho
New User
New User
Posts: 9
Joined: Sun Mar 04, 2018 3:39 pm
Location: Cianorte, PR - Brazil

Re: Add a map element on access the map with new key

Post by danilocoelho »

Micoute wrote:Define *brazil.Country_ = AddMapElement(Country(),"BR")
Micoute, thank you for replying.

I understand that by using AddMapElement explicitly there is no problem.
This is not the point I am proposing to discuss.

I recognize that I am trying to add an element to the map in an undocumented way. For me, it is clear in the documentation that the element will be added to the map when I access a new key and assign some value to any member of the structure.

At this point, I would like the opinions of PureBasic users and evaluate the possibility of improving this undefined behavior.

When I access a new key in map and do not assign value to any member of the structure, I instead get a pointer to a new element that I thought would be created, it is valid, I can access the members of the structure and assign value to any member . However, as demonstrated in this example, the allocated structure was not included in the map.

Code: Select all

; Not documented way, but similar the regular way.
; This key don't exists then new map element is created and pointer returned as expected! Exactly as AddMapElement...
Define *brazil.Country_ = Country("BR")
*brazil\name = "Brazil"
*brazil\continent = "America"

Debug "Map Element *brazil = " + *brazil

; But new map element isn't accessible 
ForEach Country()
  Debug Country()\name + " - " + Country()\continent + " | Pointer = " + @Country()
Next

Debug "FindMapElement BR = " + FindMapElement(Country(), "BR")
Debug "MapSize = " + MapSize(Country())
Could I be clear?
Sorry for my English, using google translate here.
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Add a map element on access the map with new key

Post by said »

Hi,

The common way is the documented way as mentioned by Micoute using AddMapElement()

However, i think it depends on whteher the map is on the left hand side of right hand side ... it has been discussed previousely, you can search the forums (not easy i know :? )

Look at this

Code: Select all

Structure Country_
  name.s
  continent.s
EndStructure

NewMap Country.Country_()


; Not documented way, but similar the regular way.
; This key don't exists then new map element is created and pointer returned as expected! Exactly as AddMapElement...
Define *brazil.Country_

If Country("BR")                       ; < --- this ensures the proper allocation of the structure Country_
    *brazil = Country()
EndIf

*brazil\name = "Brazil"
*brazil\continent = "America"

Debug "Map Element *brazil = " + *brazil

; But new map element isn't accessible 
ForEach Country()
  Debug ">>> " + Country()\name + " - " + Country()\continent + " | Pointer = " + @Country()
Next

Debug "FindMapElement BR = " + FindMapElement(Country(), "BR")
Debug "MapSize = " + MapSize(Country())
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Add a map element on access the map with new key

Post by nco2k »

y'all are missing the point. its a bug in my opinion:

Code: Select all

Structure MyStruc
  Foo.b
EndStructure

NewMap MyMap.MyStruc()
Debug MyMap("Test")
the map is empty, so what exactly is that pointer pointing to? the map itself? it would be better to simply return 0, to avoid confusion.

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Add a map element on access the map with new key

Post by said »

nco2k wrote:y'all are missing the point. its a bug in my opinion:

Code: Select all

Structure MyStruc
  Foo.b
EndStructure

NewMap MyMap.MyStruc()
Debug MyMap("Test")
the map is empty, so what exactly is that pointer pointing to? the map itself? it would be better to simply return 0, to avoid confusion.

c ya,
nco2k
I highly value your opinions nco2k :D they are mostly very accurate
For me this is quite confusing and i also prefer if the return was simply 0
#NULL
Addict
Addict
Posts: 1497
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Add a map element on access the map with new key

Post by #NULL »

nco2k wrote:

Code: Select all

Structure MyStruc
  Foo.b
EndStructure

NewMap MyMap.MyStruc()
Debug MyMap("Test")
the map is empty, so what exactly is that pointer pointing to?
Its not empty, the key will be added to the map. Maybe you're saying it shouldn't be added?
Anyway it doesn't make sense that in the following 'a' is added to the map but 'b' and 'c' are not:

Code: Select all

NewMap m.s()

m("a")
Debug FindMapElement(m(), "a")

b.s = m("b")
Debug FindMapElement(m(), "b")

c = @m("c")
Debug FindMapElement(m(), "c")

ForEach m()
  Debug "m: " + MapKey(m())
Next
#NULL
Addict
Addict
Posts: 1497
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Add a map element on access the map with new key

Post by #NULL »

#NULL wrote:Its not empty, the key will be added to the map.
I was wrong. You are right. I tested without the Debug i guess.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Add a map element on access the map with new key

Post by nco2k »

#NULL wrote:Its not empty, the key will be added to the map.
no it wont.

this reads the element:

Code: Select all

Debug MyMap("Test")
this sets the element:

Code: Select all

MyMap("Test")
but yea, i should have written it like this, to demonstrate the problem more properly:

Code: Select all

Structure MyStruc
  Foo.b
EndStructure

NewMap MyMap.MyStruc()

*Addr = MyMap("Test")
Debug *Addr
Debug FindMapElement(MyMap(), "Test")
Debug MapSize(MyMap())
however, this problem only occurs with structured maps, which you dont use in your example.

without a structure, the return value will simply be 0 or an empty string, depending on the type of the map:

Code: Select all

NewMap MyString$()
Debug MyString$("Foo")

NewMap MyLong.l()
Debug MyLong("Bar")
c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
danilocoelho
New User
New User
Posts: 9
Joined: Sun Mar 04, 2018 3:39 pm
Location: Cianorte, PR - Brazil

Re: Add a map element on access the map with new key

Post by danilocoelho »

Thank you nco2k for your objectivity.

My opinion is also that this is a bug.
However, because I am a newbie user in the forum I prefer to expose the situation before reporting.

By the way, how to report the bug?
Can anyone test on Mac OS and Windows?

Code: Select all

Structure MyStruc
  Foo.b
EndStructure

NewMap MyMap.MyStruc()

*Addr = MyMap("Test")
Debug *Addr
Debug FindMapElement(MyMap(), "Test")
Debug MapSize(MyMap())
I believe this happens on all platforms.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Add a map element on access the map with new key

Post by nco2k »

no thats fine. its never wrong to first discuss things in the coding section, before posting it as a bug. just go to the "Bugs - Windows" section here on the forum, and create a new topic with a short example code, and maybe even the link to this thread. the team will investigate and respond. it might take a week, it might take a year. this isnt really a high priority issue, but the team will read every bug report for sure. hell, im certain that fred already saw this thread, but stuff outside of the bugs section will easily be forgotten, as its impossible to keep track of all the discussions across the board.

or maybe one of the mods could simply move this thread to the bugs section? i would do it myself, but im not a mod. :)

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
danilocoelho
New User
New User
Posts: 9
Joined: Sun Mar 04, 2018 3:39 pm
Location: Cianorte, PR - Brazil

Re: Add a map element on access the map with new key

Post by danilocoelho »

In this topic I'm use valgrind for detect memory leak.

I investigated this bug with this example of code:

Code: Select all

Structure MyStruc
  Foo.s
  List dependencies.MyStruc()
EndStructure

Procedure main()
  If OpenConsole()
    PrintN("Basic program...") 
    Protected NewMap MyMap.MyStruc()
    Protected *new_map_element.MyStruc = MyMap("Test")
    PrintN("*new_map_element: " + *new_map_element)
    AddElement(*new_map_element\dependencies()) ; <----- memory leak here!
    *new_map_element\dependencies()\Foo = "Test"; <----- memory leak here!
    PrintN("SizeOf(*new_map_element): " + SizeOf(*new_map_element))
    PrintN("FindMapElement: " + FindMapElement(MyMap(), "Test"))
    PrintN("MapSize: " + MapSize(MyMap()))
    
    CloseConsole()
  EndIf 
EndProcedure

main()
Valgrind memory leak check:

Code: Select all

danilo@pc-linux-mint ~/workspace/gitlab/projetos_bin $ valgrind --leak-check=yes ./console_basic
==5247== Memcheck, a memory error detector
==5247== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==5247== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==5247== Command: ./console_basic
==5247== 
Basic program...
*new_map_element: 88213736
SizeOf(*new_map_element): 8
FindMapElement: 0
MapSize: 0
==5247== 
==5247== HEAP SUMMARY:
==5247==     in use at exit: 1,194 bytes in 6 blocks
==5247==   total heap usage: 44 allocs, 38 frees, 25,639 bytes allocated
==5247== 
==5247== 18 bytes in 1 blocks are possibly lost in loss record 1 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x4050C1: SYS_FastAllocateStringFree4 (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x40217C: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247==    by 0x10004157F1C02FF: ???
==5247==    by 0x170F12FF1A1311FE: ???
==5247==    by 0xFF15: ???
==5247==    by 0x541F6B7: ???
==5247==    by 0x54208E7: ???
==5247== 
==5247== 48 bytes in 1 blocks are possibly lost in loss record 2 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x40451D: SYS_AllocateMemory (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4045CC: SYS_AllocateMemoryWithSize (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x403261: PB_Object_CreateAllocator (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402BD9: PB_NewMap (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4020C5: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x541F64F: ???
==5247==    by 0x4023BC: PB_PrintN (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402095: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247==    by 0x10004157F1C02FF: ???
==5247== 
==5247== 104 bytes in 1 blocks are possibly lost in loss record 3 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x40451D: SYS_AllocateMemory (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4045CC: SYS_AllocateMemoryWithSize (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4032F9: PB_Object_CreateAllocator (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x403282: PB_Object_CreateAllocator (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402BD9: PB_NewMap (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4020C5: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x541F64F: ???
==5247==    by 0x4023BC: PB_PrintN (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402095: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247== 
==5247== 104 bytes in 1 blocks are possibly lost in loss record 4 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x40451D: SYS_AllocateMemory (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4045CC: SYS_AllocateMemoryWithSize (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x40265D: PB_NewList (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x404390: SYS_InitDynamicStructure (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x40254F: PB_AddElement (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402160: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247==    by 0x10004157F1C02FF: ???
==5247==    by 0x170F12FF1A1311FE: ???
==5247==    by 0xFF15: ???
==5247== 
==5247== 104 bytes in 1 blocks are definitely lost in loss record 5 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x40451D: SYS_AllocateMemory (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4045CC: SYS_AllocateMemoryWithSize (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x40265D: PB_NewList (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x404390: SYS_InitDynamicStructure (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402A54: PB_GetMapElement (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4020DF: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247==    by 0x10004157F1C02FF: ???
==5247==    by 0x170F12FF1A1311FE: ???
==5247==    by 0xFF15: ???
==5247== 
==5247== 816 bytes in 1 blocks are possibly lost in loss record 6 of 6
==5247==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5247==    by 0x40451D: SYS_AllocateMemory (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4045CC: SYS_AllocateMemoryWithSize (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4034C8: PB_Object_BlockAlloc (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x4024CF: PB_AddElement (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x402160: ??? (in /home/danilo/workspace/gitlab/projetos_bin/console_basic)
==5247==    by 0x500006D01: ???
==5247==    by 0x8A3B000004BE: ???
==5247==    by 0x10004157F1C02FF: ???
==5247==    by 0x170F12FF1A1311FE: ???
==5247==    by 0xFF15: ???
==5247==    by 0x541F6B7: ???
==5247== 
==5247== LEAK SUMMARY:
==5247==    definitely lost: 104 bytes in 1 blocks
==5247==    indirectly lost: 0 bytes in 0 blocks
==5247==      possibly lost: 1,090 bytes in 5 blocks
==5247==    still reachable: 0 bytes in 0 blocks
==5247==         suppressed: 0 bytes in 0 blocks
==5247== 
==5247== For counts of detected and suppressed errors, rerun with: -v
==5247== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
nco2k wrote that this is not really a high priority issue, but we should also consider the memory leak ... and the difficulty of finding this kind of bug.

Please can any moderator move this topic to the bug section?

Thank you.
Post Reply