-2

I'm using such code:

std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;

LimitOrder& newOrder = futOrders[orderId];
newOrder.Operation = side;
newOrder.InstrumentId = instrumentId;
newOrder.Lots = lots;
newOrder.Price = price;
newOrder.State = Active;
newOrder.Id = orderId;

Here I know that futOrders doesn't contain orderId, so [] works this way: the function inserts a new element with that key and returns a reference to it's mapped value.

Ideally I want this behavior: "Just add to collection, otherwise somehow indicate that element is already exist. Exception is preffered." I want this to make it clear in code that it's assumed that element is new, and probably for better performance. Can you recomend something or what I have is the best?

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
Oleg Vazhnev
  • 23,239
  • 54
  • 171
  • 305
  • 1
    http://stackoverflow.com/questions/326062/in-stl-maps-is-it-better-to-use-mapinsert-than – Retired Ninja Apr 20 '14 at 09:00
  • 2
    Didn't you get the answers in your previous question? – jrok Apr 20 '14 at 09:00
  • difference is that here i KNOW that order doesn't exist and i want to USE IT probably for better performance. In my first question I don't know if order exist. – Oleg Vazhnev Apr 20 '14 at 09:08
  • If you KNOW it doesn't exist then you don't have to do anything special. Just use `operator[]`. – juanchopanza Apr 20 '14 at 09:15
  • 1
    For an operation to indicate that the value was already present, it must check whether the value was already present. This means that the operation cannot have a performance advantage. Even if you allow undefined behaviour when the element already exists, there is still not much room for optimisation, as to insert an element, the container must find where to put that element. For these reasons, standard C++ containers do not provide such an operation, if you want this operation, you should write your own container.(If you just want to make the intent of the code clear, write a helper function) – Mankarse Apr 20 '14 at 09:18
  • @juanchopanza also I want to show code reader that I know element doesn't exist :) `C#` for example throws an exception if I call `Add` method and element already exist. http://msdn.microsoft.com/en-us/library/k7z0zy8k(v=vs.110).aspx – Oleg Vazhnev Apr 20 '14 at 09:31
  • well I think i want the same as C# `Dictionary.Add`, and without copy-constructor that `insert` might call – Oleg Vazhnev Apr 20 '14 at 09:31
  • That makes sense. My answer has a simple way of throwing if the element already exists, without copy constructing or trying to inset anything. – juanchopanza Apr 20 '14 at 09:32
  • @juanchopanza your answer is too slow – Oleg Vazhnev Apr 20 '14 at 09:34
  • @javapowered: If you use `emplace` rather than `insert`, you can avoid calling the copy constructor. – Mankarse Apr 20 '14 at 09:34
  • 1
    @javapowered Oh really? Do you have benchmarks for that? At some point you are going to need a loop-up, there's no way around that. – juanchopanza Apr 20 '14 at 09:35
  • @juanchopanza you doing extra work (count) to check somehting that I know. I know that element doesn't exist so I don't need to `count`. – Oleg Vazhnev Apr 20 '14 at 09:37
  • A) does the extra work have a perceptible impact? B) As I said before, if you know then you don't need to take a special action. If you want the reader of the code to know you know, put a comment `// I know`. – juanchopanza Apr 20 '14 at 09:40

7 Answers7

2

You can use emplace:

std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;

auto i = futOrders.emplace(
           std::piecewise_construct, std::tie(orderId), std::make_tuple());
if (i.second) {
    auto &newOrder = i.first->second;
    //newOrder is a newly inserted order
}
else {
    //order already present at orderId...
}

This can be wrapped in a helper function

template<typename Map, typename ...Args>
typename Map::mapped_type &emplace_new_element(Map &&map, Args &&...args) {
    auto i = std::forward<Map>(map).emplace(std::forward<Args>(args)...);
    assert(i.second); 
    return i.first->second;
}

Usage:

auto &newOrder = emplace_new_element(
    futOrders,
    std::piecewise_construct, std::tie(orderId), std::make_tuple());
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • I know that element doesn't exist and so I don't want to write too much code to check it. I would prefer exception or UB for the case when element exist. – Oleg Vazhnev Apr 20 '14 at 09:36
  • @javapowered: See my edit, which includes a useful helper function. – Mankarse Apr 20 '14 at 09:41
1

Just throw if the element is there:

if (fitOrders.count(orderID))
{
  // throw your preferred exception
} 

// insert the order
LimitOrder& newOrder = futOrders[orderId];
....
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
1

map::insert returns a pair containing an iterator to the element with the given key and a boolean which indicates whether the new element was inserted.

std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;

LimitOrder newOrder;

// Initialize newOrder..

bool inserted = futOrders.insert( make_pair( orderId, newOrder) ).second;

if (!inserted) 
{
    // Element was not inserted because there is already another one with the same key
    // Throw an exception here or do whatever you like
}

This works well if the cost of initialize LimitOrder is negligible. Otherwise you should use find to check if an element with the given key already exists and then use insert if find returns map::end().

sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • here you create LimitOrder twice because `insert` will call copy-constructor. In my design it is not acceptable to create same order twice. – Oleg Vazhnev Apr 20 '14 at 09:06
1

You should use std::unordered_map::insert

std::pair<iterator,bool> insert( const value_type& value );

Returns a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place.

4pie0
  • 29,204
  • 9
  • 82
  • 118
0

Similar to my suggestion before:

LimitOrder& newOrder = futOrders[orderId];
if (!newOrder.empty())
{
    // do stuff that says order is not empty as you expect. 
}
else
{
    // fill in order. 
}
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

Neither of these throw an exception but both insert and emplace (C++11) return a pair of an iterator and a bool which tells you if it already existed.

Joe
  • 6,497
  • 4
  • 29
  • 55
0

if you don't care about what is there or what is inserted then use something like,

if(futOrders.count(orderId)>0) {
     // exists, signal it as you desire
} else {
     .... // your insertion code
}

if you are interested in returning what it is in there or what you have inserted, then use find and insert both will get you an iterator only indexing once

jsantander
  • 4,972
  • 16
  • 27