I just wanted to know how deque is implemented and how are the basic operations like push_front and random access operator are provided in that implementation.
I just wanted to know how deque is implemented
It's always a good to have an excuse for doing ASCII art:
+-------------------------------------------------------------+
| std::deque<int> |
| |
| subarrays: |
| +---------------------------------------------------------+ |
| | | | | | | |
| | int(*)[8] | int(*)[8] | int(*)[8] |int(*)[8]|int(*)[8] | |
| | | | | | | |
| +---------------------------------------------------------+ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| - - |
| +------------------------------+ |
| | ?, ?, 42, 43, 50, ?, ?, ?, ? | |
| +------------------------------+ |
| |
| additional state: |
| |
| - pointer to begin of the subarrays |
| - current capacity and size |
| - pointer to current begin and end |
+-------------------------------------------------------------+
how are the basic operations like push_front and random access operator are provided in that implementation?
First, std::deque::push_front, from libcxx:
template <class _Tp, class _Allocator>
void
deque<_Tp, _Allocator>::push_front(const value_type& __v)
{
allocator_type& __a = __base::__alloc();
if (__front_spare() == 0)
__add_front_capacity();
__alloc_traits::construct(__a, _VSTD::addressof(*--__base::begin()), __v);
--__base::__start_;
++__base::size();
}
This obviously checks whether the memory already allocated at the front can hold an additional element. If not, it allocates. Then, the main work is shifted to the iterator: _VSTD::addressof(*--__base::begin()) goes one location before the current front element of the container, and this address is passed to the allocator to construct a new element in place by copying v (the default allocator will definitely do a placement-new).
Now random access. Again from libcxx, std::deque::operator[] (the non-const version) is
template <class _Tp, class _Allocator>
inline
typename deque<_Tp, _Allocator>::reference
deque<_Tp, _Allocator>::operator[](size_type __i) _NOEXCEPT
{
size_type __p = __base::__start_ + __i;
return *(*(__base::__map_.begin() + __p / __base::__block_size) + __p % __base::__block_size);
}
This pretty much computes an index relative to some start index, and then determines the subarray and the index relative to the start of the subarray. __base::__block_size should be the size of one subarray here.
Related
I've been adding std::string_views to some old code for representing string like config params, as it provides a read only view, which is faster due to no need for copying.
However, one cannot concatenate two string_view together as the operator+ isn't defined. I see this question has a couple answers stating its an oversight and there is a proposal in for adding that in. However, that is for adding a string and a string_view, presumably if that gets implemented, the resulting concatenation would be a std::string
Would adding two string_view also fall in the same category? And if not, why shouldn't adding two string_view be supported?
Sample
std::string_view s1{"concate"};
std::string_view s2{"nate"};
std::string_view s3{s1 + s2};
And here's the error
error: no match for 'operator+' (operand types are 'std::string_view' {aka 'std::basic_string_view<char>'} and 'std::string_view' {aka 'std::basic_string_view<char>'})
A view is similar to a span in that it does not own the data, as the name implies it is just a view of the data. To concatenate the string views you'd first need to construct a std::string then you can concatenate.
std::string s3 = std::string(s1) + std::string(s2);
Note that s3 will be a std::string not a std::string_view since it would own this data.
A std::string_view is an alias for std::basic_string_view<char>, which is a std::basic_string_view templated on a specific type of character, i.e. char.
But what does it look like?
Beside the fairly large number of useful member functions such as find, substr, and others (maybe it's an ordinary number, if compared to other container/string-like things offered by the STL), std::basic_string_view<_CharT>, with _CharT being the generic char-like type, has just 2 data members,
// directly from my /usr/include/c++/12.2.0/string_view
size_t _M_len;
const _CharT* _M_str;
i.e. a constant pointer to _CharT to indicate where the view starts, and a size_t (an appropriate type of number) to indicate how long the view is starting from _M_str's pointee.
In other words, a string view just knows where it starts and how long it is, so it represents a sequence of char-like entities which are consecutive in memory. With just two such memebrs, you can't represent a string which is made up of non-contiguous substrings.
Yet in other words, if you want to create a std::string_view, you need to be able to tell how many chars it is long and from which position. Can you tell where s1 + s2 would have to start and how many characters it should be long? Think about it: you can't, becase s1 and s2 are not adjacent.
Maybe a diagram can help.
Assume these lines of code
std::string s1{"hello"};
std::string s2{"world"};
s1 and s2 are totally unrelated objects, as far as their memory location is concerned; here is what they looks like:
&s2[0]
|
| &s2[1]
| |
&s1[0] | | &s2[2]
| | | |
| &s1[1] | | | &s2[3]
| | | | | |
| | &s1[2] | | | | &s2[4]
| | | | | | | |
| | | &s1[3] v v v v v
| | | | +---+---+---+---+---+
| | | | &s1[4] | w | o | r | l | d |
| | | | | +---+---+---+---+---+
v v v v v
+---+---+---+---+---+
| h | e | l | l | o |
+---+---+---+---+---+
I've intentionally drawn them misaligned to mean that &s1[0], the memory location where s1 starts, and &s2[0], the memory location where s2 starts, have nothing to do with each other.
Now, imagine you create two string views like this:
std::string_view sv1{s1};
std::string_view sv2(s2.begin() + 1, s2.begin() + 4);
Here's what they will look like, in terms of the two implementation-defined members _M_str and _M_len:
&s2[0]
|
| &s2[1]
| |
&s1[0] | | &s2[2]
| | | |
| &s1[1] | | | &s2[3]
| | | | | |
| | &s1[2] | | | | &s2[4]
| | | | | | | |
| | | &s1[3] v v v v v
| | | | +---+---+---+---+---+
| | | | &s1[4] | w | o | r | l | d |
| | | | | +---+---+---+---+---+
v v v v v · ^ ·
+---+---+---+---+---+ · | ·
| h | e | l | l | o | +---+ ·
+---+---+---+---+---+ | · ·
· ^ · | · s2._M_len ·
· | · | <----------->
+---+ · |
| · · +-- s2._M_str
| · s1._M_len ·
| <------------------->
|
+-------- s1._M_str
Given the above, can you see what's wrong with expecting that
std::string_view s3{s1 + s2};
works?
How can you possible define s3._M_str and s3._M_len (based on s1._M_str, s1._M_len, s2._M_str, and s2._M_len), such that they represent a view on "helloworld"?
You can't because "hello" and "world" are located in two unrelated areas of memory.
std::string_view does not own any data, it is only a view. If you want to join two views to get a joined view, you can use boost::join() from the Boost library. But result type will be not a std::string_view.
#include <iostream>
#include <string_view>
#include <boost/range.hpp>
#include <boost/range/join.hpp>
void test()
{
std::string_view s1{"hello, "}, s2{"world"};
auto joined = boost::join(s1, s2);
// print joined string
std::copy(joined.begin(), joined.end(), std::ostream_iterator(std::cout, ""));
std::cout << std::endl;
// other method to print
for (auto c : joined) std::cout << c;
std::cout << std::endl;
}
C++23 has joined ranges in the standard library with the name of std::ranges::views::join_with_view
#include <iostream>
#include <ranges>
#include <string_view>
void test()
{
std::string_view s1{"hello, "}, s2{"world"};
auto joined = std::ranges::views::join_with_view(s1, s2);
for (auto c : joined) std::cout << c;
std::cout << std::endl;
}
I'm struggling with the correct mental model and understanding of std::vector.
What I thought I knew
When you create a vector of type T and then reserve N elements for the vector, the compiler basically finds and reserves a contiguous block of memory that is N * sizeof(T) bytes. For example,
// Initialize a vector of int
std::vector<int> intvec;
// Reserve contigious block of 4 4-byte chunks of memory
intvec.reserve(4); // [ | | | ]
// Filling in the memory chunks has obvious behavior:
intvec.push_back(1); // [1| | | ]
intvec.push_back(2); // [1|2| | ]
Then we can access any element in random access time because, if we ask for the kth element of the vector, we simply start at the memory address of the start of the vector and then "jump" k * sizeof(T) bytes to get to the kth element.
Custom Objects
My mental model breaks down for custom objects of unknown/varying size. For example,
class Foo {
public:
Foo() = default;
Foo(std::vector<int> vec): _vec{vec} {}
private:
std::vector<int> _vec;
};
int main() {
// Initialize a vector Foo
std::vector<Foo> foovec;
// Reserve contigious block of 4 ?-byte chunks of memory
foovec.reserve(4); // [ | | | ]
// How does memory allocation work since object sizes are unkown?
foovec.emplace_back(std::vector<int> {1,2}); // [{1,2}| | | ]
foovec.emplace_back(std::vector<int> {1,2,3,4,5}); // [{1,2}|{1,2,3,4,5}| | ]
return 0;
}
Since we don't know the size of each instance of Foo, how does foovec.reserve() allocate memory? Furthermore, how could you achieve random access time we don't know how far to "jump" to get to the kth element?
Your concept of size is flawed. A std::vector<type> has a compile time known size of space it is going to take up. It also has a run time size that it may use (this is allocated at run time and the vector holds a pointer to it). You can picture it laid out like
+--------+
| |
| Vector |
| |
| |
+--------+
|
|
v
+-------------------------------------------------+
| | | | | |
| Element | Element | Element | Element | Element |
| | | | | |
+-------------------------------------------------+
So when you have a vector of things that have a vector in them, each Element becomes the vector and then those point of to their own storage somewhere else like
+--------+
| |
| Vector |
| |
| |
+----+---+
|
|
v
+----+----+---------+---------+
| Object | Object | Object |
| with | with | with |
| Vector | Vector | Vector |
+----+----+----+----+----+----+
| | | +---------+---------+---------+---------+---------+
| | | | | | | | |
| | +--->+ Element | Element | Element | Element | Element |
| | | | | | | |
| | +-------------------------------------------------+
| | +-------------------------------------------------+
| | | | | | | |
| +--->+ Element | Element | Element | Element | Element |
| | | | | | |
| +-------------------------------------------------+
| +-------------------------------------------------+
| | | | | | |
+--->+ Element | Element | Element | Element | Element |
| | | | | |
+---------+---------+---------+---------+---------+
This way all of the vectors are next to each other, but the elements the vectors have can be anywhere else in memory. It is for this reason you don't want to use a std:vector<std::vector<int>> for a matrix. All of the sub vectors get memory to wherever so there is no locality between the rows.
Do note that this applies to all of the allocator aware containers as they do not store the elements inside the container directly. This is not true for std::array as, like a raw array, the elements are part of the container. If you have an std::array<int, 20> then it is at least sizeof(int) * 20 bytes in size.
the size of
class Foo {
public:
Foo() = default;
Foo(std::vector<int> vec): _vec{vec} {}
private:
std::vector<int> _vec;
};
is known and constant, the internal std::vector does the allocation in the heap, so there is no problem to do foovec.reserve(4);
else how a std::vector can be in the stack ? ;-)
The size of your class Foo is known at compile time, the std::vector class has a constant size, as the elements that it hold are allocated on the heap.
std::vector<int> empty{};
std::vector<int> full{};
full.resize(1000000);
assert(sizeof(empty) == sizeof(full));
Both instances of std::vector<int>, empty and full will always have the same size despite holding a different number of elements.
If you want an array which you can not resize, and it's size must be known at compile time, use std::array.
When you create a vector of type T and then reserve N elements for the vector, the compiler basically finds and reserves a contiguous block of memory
The compiler does no such thing. It generates code to request storage from the vector's allocator at runtime. By default this is std::allocator, which delegates to operator new, which will fetch uninitialized storage from the runtime system.
My mental model breaks down for custom objects of unknown/varying size
The only way a user-defined type can actually have unknown size is if it is incomplete - and you can't declare a vector to an incomplete type.
At any point in your code where the type is complete, its size is also fixed, and you can declare a vector storing that type as usual.
Your Foo is complete, and its size is fixed at compile time. You can check this with sizeof(Foo), and sizeof(foovec[0]) etc.
The vector owns a variable amount of storage, but doesn't contain it in the object. It just stores a pointer and the reserved & used sizes (or something equivalent). For example, an instance of:
class toyvec {
int *begin_;
int *end_;
size_t capacity_;
public:
// push_back, begin, end, and all other methods
};
always has fixed size sizeof(toyvec) = 2 * sizeof(int*) + sizeof(size_t) + maybe_some_padding. Allocating a huge block of memory, and setting begin to the start of it, has no effect on the size of the pointer itself.
tl;dr C++ does not have dynamically-resizing objects. The size of an object is fixed permanently by the class definition. C++ does have objects which own - and may resize - dynamic storage, but that isn't part of the object itself.
While using Spock i can do something like this:
when:
12.times {mailSender.send("blabla", "subject", "content")}
then:
12 * javaMailSender.send(_)
When i tried to do same in Mockito:
verify(javaMailSender,times(12)).send(any(SimpleMailMessage.class))
I got an error that SimpleMailMessage has null values, so i had to initialize it in test:
SimpleMailMessage simpleMailMessage = new SimpleMailMessage()
simpleMailMessage.setTo("blablabla")
simpleMailMessage.subject = "subject"
simpleMailMessage.text = "content"
verify(javaMailSender,times(12)).send(simpleMailMessage))
Now it works but it's a large workload and i really don't care about equality. What if SimpleMailMessage will have much more arguments or another objects with another arguments, meh. Is there any way to check that send method was just called X times?
EDIT: added implementation of send method.
private fun sendEmail(recipient: String, subject: String, content: String)
{
val mailMessage = SimpleMailMessage()
mailMessage.setTo(recipient)
mailMessage.subject = subject
mailMessage.text = content
javaMailSender.send(mailMessage)
}
There are 2 senders, mailSender is my custom object and javaMailSender is from another libary
Stacktrace:
Mockito.verify(javaMailSender,
Mockito.times(2)).send(Mockito.any(SimpleMailMessage.class))
| | | | |
| | | | null
| | | Wanted but not invoked:
| | | javaMailSender.send(
| | | <any org.springframework.mail.SimpleMailMessage>
| | | );
| | | -> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
| | |
| | | However, there were exactly 2 interactions with this mock:
| | | javaMailSender.send(
| | | SimpleMailMessage: from=null; replyTo=null; to=blabla; cc=; bcc=; sentDate=null; subject=subject; text=content
| | | );
| | | -> at MailSenderServiceImpl.sendEmail(MailSenderServiceImpl.kt:42)
| | |
| | | javaMailSender.send(
| | | SimpleMailMessage: from=null; replyTo=null; to=blabla; cc=; bcc=; sentDate=null; subject=subject; text=content
| | | );
If you don't care for the parameter of send, leave any() empty:
verify(javaMailSender,times(12)).send(any())
I'm using the building tool of dojo to generate a single file dojo.js, but I don't know why I'm getting multiple files.
This is my example profile:
var profile = (function(){
return {
basePath: "../../../",
releaseDir: "./app",
releaseName: "lib",
action: "release",
layerOptimize: "closure",
optimize: "closure",
mini: true,
stripConsole: "warn",
selectorEngine: "lite",
defaultConfig: {
hasCache:{
"dojo-built": 1,
"dojo-loader": 1,
"dom": 1,
"host-browser": 1,
"config-selectorEngine": "lite"
},
async: 1
},
staticHasFeatures: {
'dojo-trace-api': 0,
'dojo-log-api': 0,
'dojo-publish-privates': 0,
'dojo-sync-loader': 0,
'dojo-test-sniff': 0
},
packages:['dojo'],
layers: {
"dojo/dojo": {
include: ["dojo/domReady"],
customBase: true,
boot: true
}
}
};
})();
This is my .bat:
./util/buildscripts/build profile=cgl-dojo
After execute it, this is the release folder:
app
\---lib
\---dojo
+---cldr
| \---nls
| +---ar
| +---ca
| +---cs
| +---da
| +---de
| +---el
| +---en
| +---en-au
| +---en-ca
| +---en-gb
| +---es
| +---fi
| +---fr
| +---fr-ch
| +---he
| +---hu
| +---it
| +---ja
| +---ko
| +---nb
| +---nl
| +---pl
| +---pt
| +---pt-pt
| +---ro
| +---ru
| +---sk
| +---sl
| +---sv
| +---th
| +---tr
| +---zh
| +---zh-hant
| +---zh-hk
| \---zh-tw
+---data
| +---api
| \---util
+---date
+---dnd
+---errors
+---fx
+---io
+---nls
| +---ar
| +---az
| +---bg
| +---ca
| +---cs
| +---da
| +---de
| +---el
| +---es
| +---fi
| +---fr
| +---he
| +---hr
| +---hu
| +---it
| +---ja
| +---kk
| +---ko
| +---nb
| +---nl
| +---pl
| +---pt
| +---pt-pt
| +---ro
| +---ru
| +---sk
| +---sl
| +---sv
| +---th
| +---tr
| +---uk
| +---zh
| \---zh-tw
+---promise
+---request
+---resources
| \---images
+---router
+---rpc
+---selector
+---store
| +---api
| \---util
+---_base
\---_firebug
I need a release folder with only one file, please help me.
The entire tree of registered packages is always built because the build tool has no way of knowing whether or not you are conditionally requiring other modules within your application. There is no way to make the build system only output one file, and in fact a single file is a bad idea because each locale has its own set of localisation rules. If you want to reduce the number of files after a build, you can just delete all the ones you don’t want.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to pass objects to functions in C++?
Main class
#include "List.h"
#include "Car.h"
#include "Worker.h"
#include "Queue.h"
#include <iostream>
#include <string>
using namespace std;
void initWorkerList(List<Worker>);
void initCarList(List<Car>, Queue, Queue);
int main() {
List<Worker> WorkerList;
List<Car> CarList;
Queue q1, q2;
initWorkerList(WorkerList);
initCarList(CarList, q1, q2); // Error here
//..... e.g cout << "Successful!"; but it does not displays it...
}
void initWorkerList(List<Worker> WorkerList) {
Worker w1 = Worker("Ben Ang", "Ben123", "pass123", 'M');
WorkerList.add(w1);
Worker w2 = Worker("Grace Eng", "Gr4ce", "loveGrace", 'W');
WorkerList.add(w2);
Worker w3 = Worker("Rebecca Xuan", "Xuanz", "Rebecca Xuan", 'W');
WorkerList.add(w3);
}
void initCarList(List<Car> CarList, Queue q1, Queue q2) {
Car c1 = Car("SJS1006Z","Toyota", "Saloon car");
Car c2 = Car("SFW6666E", "hyundai", "Taxi (Saloon)");
Car c3 = Car("SCF1006G","Mercedes", "Large Van");
Car c4 = Car("SBQ1006Z", "Puma", "Saloon Car");
q1.enqueue(c1);
q2.enqueue(c1);
q2.enqueue(c3);
q1.enqueue(c4);
q1.enqueue(c1);
q1.enqueue(c1);
q1.enqueue(c1);
q2.enqueue(c2);
q2.enqueue(c2);
}
There is no error at all. But nothing is displayed when being debugged...I have tried and my guess is there is something wrong with initCarList(CarList,q1,q2); cause after that code, other codes can work at all. Is there anything wrong with it? Thanks
You are passing the Queue Variables by value rather than by reference.
initCarList(CarList, q1, q2); // Error here
So any change in initCarList wont get reflected back to caller
Change your function signature to
void initCarList(List<Car> CarList, Queue& q1, Queue& q2) {
and the declaration to
void initCarList(List<Car>, Queue&, Queue&);
If you pass parameter by value, any change within initCarList is local to the function scope and does not get reflected back.
Pass by Value
Caller Callee
|------| |------|
workedList workedList
| ___ | | ___ |
|| | |--------> || | | <------
||___| | ||___| | |
| | | | |
|q1 | |q1 | (Changing any of these variables
| ___ | | ___ | won't be reflected back)
|| | |--------> || | | |
||___| | ||___| | |
| | | | |
|q2 | |q2 | |
| ___ | | ___ | |
|| | |--------> || | | |
||___| | ||___| | <------
|______| |______|
Pass by reference
Caller Callee
-------- --------
|wList | |wList |
| ___ | | ____ |
|| | |--------> || || <------
||___|<|------------||-*__|| |
| _ | | | |
|q1 | |q1 | (Changing any of these variables
| ___ | | ____ | will be reflected back)
|| | |--------> || || |
||___|<|------------||-*__|| |
| | | | |
|q2 | |q2 | |
| ___ | | ____ | |
|| | |--------> || || |
||___|<|------------||-*__|| <------
|______| |______|
You're passing your variables in by value, which means that the function's parameters hold a copy of them, which you modify and discard when the function ends. Pass by reference instead to modify the original variable. For example, initCarList would become:
void initCarList(List<Car> CarList, Queue &q1, Queue &q2)
You also don't use the CarList parameter, so you might as well take it out if this is how it is in your code.
Your functions pass by value which means functions make a copy of passed in variables and manipulate on the copied ones. To modify on original ones you need to pass parameter by reference
Change:
void initWorkerList(List<Worker>);
void initCarList(List<Car>, Queue, Queue);
To:
void initWorkerList(List<Worker> &);
void initCarList(List<Car>&, Queue&, Queue&);