473,794 Members | 2,738 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

sets with different comparators have same iterator type; standard?

is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

note that the types of the _sets_ in both sides of assignment[*] are
different due to their different comparators (Asc/Des). nevertheless,[*] seems to be standard as the template parameters of the iterator
are 'Key' and 'Distance' (not the 'Comparator'), and so the iterator
types are equal, right? [we use user defined template 'Comparator'
parameters (Des/Asc), so they don't have standard specialization,
which means we can be sure the 'Distance' type of the two iterators
is the same; the 'Key' type (int) is also obviously the same].

thanks,
--dan
Oct 27 '05 #1
13 2541
Dan Tsafrir wrote:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

note that the types of the _sets_ in both sides of assignment[*] are
different due to their different comparators (Asc/Des). nevertheless,
[*] seems to be standard as the template parameters of the iterator
are 'Key' and 'Distance' (not the 'Comparator'), and so the iterator
types are equal, right? [we use user defined template 'Comparator'
parameters (Des/Asc), so they don't have standard specialization,
which means we can be sure the 'Distance' type of the two iterators
is the same; the 'Key' type (int) is also obviously the same].

thanks,
--dan


This code is fine. std::copy does not require that the two iterators be
of the same type. In fact, they need not even belong to containers that
hold identical types - as long as the type being copied is convertible
to the output iterator's type.

So in this example, you could copy a set<int> to a set<long> or even a
set<double> and still be OK.

Greg

Oct 27 '05 #2
Dan Tsafrir wrote:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}


Note 1: You Asc and Des functors have identical behavior.

Note 2: operator() in Asc and Des should be const methods, and
Asc and Desc should probably inherit from std::binary_fun ction<>.

e.g.:

#include <functional>

struct Asc: public std::binary_fun ction<int,int,b ool>
{
bool operator()(int a, int b) const { return a < b; }
}

struct Des: public std::binary_fun ction<int,int,b ool>
{
// assumes you didn't want identical behavior for
// Asc and Des
bool operator()(int a, int b) const { return a > b; }
}

Oct 27 '05 #3
Greg wrote:
Dan Tsafrir wrote:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

This code is fine. std::copy does not require that the two iterators be
of the same type. In fact, they need not even belong to containers that
hold identical types - as long as the type being copied is convertible
to the output iterator's type.

So in this example, you could copy a set<int> to a set<long> or even a
set<double> and still be OK.


the question is not about copy(), it's about[*] (the copy() is just there
to make the program meaningful). the problem with[*] is that the program
assigns an object of the type: set<int,Asc>::i terator
to an object of the type : set<int,Dec>::i terator

thanks,
--dan
Oct 27 '05 #4
red floyd wrote:
Dan Tsafrir wrote:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

Note 1: You Asc and Des functors have identical behavior.


right you are (my mistake). nevertheless, they are different types.
regardless the question is about[*] in which the program assigns an
object of the type : set<int,Asc>::i terator
to an object of the type: set<int,Dec>::i terator


Note 2: operator() in Asc and Des should be const methods, and
Asc and Desc should probably inherit from std::binary_fun ction<>.

e.g.:

#include <functional>

struct Asc: public std::binary_fun ction<int,int,b ool>
{
bool operator()(int a, int b) const { return a < b; }
}

struct Des: public std::binary_fun ction<int,int,b ool>
{
// assumes you didn't want identical behavior for
// Asc and Des
bool operator()(int a, int b) const { return a > b; }
}


unfortunately, this too is not related to my original question :(

--dan
Oct 27 '05 #5

Dan Tsafrir wrote:
red floyd wrote:
Dan Tsafrir wrote:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

Note 1: You Asc and Des functors have identical behavior.


right you are (my mistake). nevertheless, they are different types.
regardless the question is about[*] in which the program assigns an
object of the type : set<int,Asc>::i terator
to an object of the type: set<int,Dec>::i terator


Note 2: operator() in Asc and Des should be const methods, and
Asc and Desc should probably inherit from std::binary_fun ction<>.

e.g.:

#include <functional>

struct Asc: public std::binary_fun ction<int,int,b ool>
{
bool operator()(int a, int b) const { return a < b; }
}

struct Des: public std::binary_fun ction<int,int,b ool>
{
// assumes you didn't want identical behavior for
// Asc and Des
bool operator()(int a, int b) const { return a > b; }
}


unfortunately, this too is not related to my original question :(


I believe the answer is implementation-dependent depending on how
set<>::iterator is declared. If the code compiles - then the iterators
would have to be of the same type (usually declared in a red-black tree
class). Therefore since the code compiles for you, the assignments are
OK. I would just not expect them to be portable however.

Greg

Oct 27 '05 #6
TIT
Dan Tsafrir sade:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

note that the types of the _sets_ in both sides of assignment[*] are
different due to their different comparators (Asc/Des). nevertheless,
[*] seems to be standard as the template parameters of the iterator
are 'Key' and 'Distance' (not the 'Comparator'), and so the iterator
types are equal, right? [we use user defined template 'Comparator'
parameters (Des/Asc), so they don't have standard specialization,
which means we can be sure the 'Distance' type of the two iterators
is the same; the 'Key' type (int) is also obviously the same].

thanks,
--dan


The standard defines the implementation of iterators as implementation
defined. The above code doesn't compile with RW since that
implemenation of the underlying tree class is defined as:

template <class _Key, class _Val, class _KeyOf, class _Comp, class _Alloc>
class __rb_tree {
class iterator : public __it {};
};

but with STLport the class iterator is not nested within their
underlying tree class:

template <class _Value, class _Traits>
struct _Rb_tree_iterat or : public _Rb_tree_base_i terator
{ };

template <class _Key, class _Value, class _KeyOfValue, class _Compare,
_STLP_DEFAULT_A LLOCATOR_SELECT (_Value) >
class _Rb_tree : public _Rb_tree_base<_ Value, _Alloc> {

typedef _Rb_tree_iterat or<value_type, _Nonconst_trait s<value_type> >
iterator;

};

And _Compare is not used with the iterator template so your
code will compile using this implementation.

TIT
Oct 27 '05 #7
TIT wrote:
Dan Tsafrir sade:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

note that the types of the _sets_ in both sides of assignment[*] are
different due to their different comparators (Asc/Des). nevertheless,
[*] seems to be standard as the template parameters of the iterator
are 'Key' and 'Distance' (not the 'Comparator'), and so the iterator
types are equal, right? [we use user defined template 'Comparator'
parameters (Des/Asc), so they don't have standard specialization,
which means we can be sure the 'Distance' type of the two iterators
is the same; the 'Key' type (int) is also obviously the same].

thanks,
--dan

The standard defines the implementation of iterators as implementation
defined. The above code doesn't compile with RW since that
implemenation of the underlying tree class is defined as:

template <class _Key, class _Val, class _KeyOf, class _Comp, class _Alloc>
class __rb_tree {
class iterator : public __it {};
};

but with STLport the class iterator is not nested within their
underlying tree class:

template <class _Value, class _Traits>
struct _Rb_tree_iterat or : public _Rb_tree_base_i terator
{ };

template <class _Key, class _Value, class _KeyOfValue, class _Compare,
_STLP_DEFAULT_A LLOCATOR_SELECT (_Value) >
class _Rb_tree : public _Rb_tree_base<_ Value, _Alloc> {

typedef _Rb_tree_iterat or<value_type, _Nonconst_trait s<value_type> >
iterator;

};

And _Compare is not used with the iterator template so your
code will compile using this implementation.

TIT


Ok, I understand, it's implementation dependent.
However, wouldn't it be great had such an iterator assignment been standard?
I'm suggesting this because it would have allowed iteration over a collection
of objects sorted according to an arbitrary criterion, without the overhead of
virtual function calls:

For example, assume you have a collection of N "Job" objects sorted
according to various criteria:

set<Job,cmp_run time> s1; // holds N jobs
set<Job,cmp_arr ival> s2; // holds the same N jobs
... // etc.

And you want some loop to iterate through the job collection according
to a criterion which is specified by a user parameter 'p'. Wouldn't being
able to do the following be very beneficial in terms of performance:

set<Job>::const _iterator beg, end;
if( p == RUNTIME ) {
beg = s1.begin();
end = s1.end();
}
else if( p == ARRIVAL ) {
beg = s2.begin();
end = s2.end();
}
else if( ... ) {
// ...
} // ...

for(set<Job>::c onst_iterator i=beg; i!=end; ++i) {
// do whatever...
}

Regrettably, the only alternative I can think of that will obtain the
above, involves inheritance and virtual function calls that turn out
to be very expensive in my application (the above loop is its kernel).

What do you think? I personally can't think of any reason why the above
shouldn't be made possible/standard. Am I missing something? is there
some other simple alternative I am not aware of?

thanks,
--dan
Oct 27 '05 #8

Dan Tsafrir wrote:
TIT wrote:
Dan Tsafrir sade:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}

note that the types of the _sets_ in both sides of assignment[*] are
different due to their different comparators (Asc/Des). nevertheless,
[*] seems to be standard as the template parameters of the iterator
are 'Key' and 'Distance' (not the 'Comparator'), and so the iterator
types are equal, right? [we use user defined template 'Comparator'
parameters (Des/Asc), so they don't have standard specialization,
which means we can be sure the 'Distance' type of the two iterators
is the same; the 'Key' type (int) is also obviously the same].

thanks,
--dan

The standard defines the implementation of iterators as implementation
defined. The above code doesn't compile with RW since that
implemenation of the underlying tree class is defined as:

template <class _Key, class _Val, class _KeyOf, class _Comp, class _Alloc>
class __rb_tree {
class iterator : public __it {};
};

but with STLport the class iterator is not nested within their
underlying tree class:

template <class _Value, class _Traits>
struct _Rb_tree_iterat or : public _Rb_tree_base_i terator
{ };

template <class _Key, class _Value, class _KeyOfValue, class _Compare,
_STLP_DEFAULT_A LLOCATOR_SELECT (_Value) >
class _Rb_tree : public _Rb_tree_base<_ Value, _Alloc> {

typedef _Rb_tree_iterat or<value_type, _Nonconst_trait s<value_type> >
iterator;

};

And _Compare is not used with the iterator template so your
code will compile using this implementation.

TIT


Ok, I understand, it's implementation dependent.
However, wouldn't it be great had such an iterator assignment been standard?
I'm suggesting this because it would have allowed iteration over a collection
of objects sorted according to an arbitrary criterion, without the overhead of
virtual function calls:

For example, assume you have a collection of N "Job" objects sorted
according to various criteria:

set<Job,cmp_run time> s1; // holds N jobs
set<Job,cmp_arr ival> s2; // holds the same N jobs
... // etc.

And you want some loop to iterate through the job collection according
to a criterion which is specified by a user parameter 'p'. Wouldn't being
able to do the following be very beneficial in terms of performance:

set<Job>::const _iterator beg, end;
if( p == RUNTIME ) {
beg = s1.begin();
end = s1.end();
}
else if( p == ARRIVAL ) {
beg = s2.begin();
end = s2.end();
}
else if( ... ) {
// ...
} // ...

for(set<Job>::c onst_iterator i=beg; i!=end; ++i) {
// do whatever...
}

Regrettably, the only alternative I can think of that will obtain the
above, involves inheritance and virtual function calls that turn out
to be very expensive in my application (the above loop is its kernel).

What do you think? I personally can't think of any reason why the above
shouldn't be made possible/standard. Am I missing something? is there
some other simple alternative I am not aware of?


The Comparator is a property of the container and not its iterator. In
other words, the Container arranges the items in a particular order and
uses the Comparator to determine that order. So any iterator that
traverses the container from beginning to end will encounter each
contained item in a just one specific order. Since the items can be
sorted in only one order per container at a time, it would take more
than just a different iterator to traverse the items in a different
order.

Since the iterators of one container can be elements in another
Container, one solution would be to declare, say, a list with the
elements in one order, and then declare another list populated with the
iterators (or elements) of the first list, but sorted in a different
order. There would be some extra management involved in keeping the
sorted lists, but some savings as well gained by essentially sharing
the elements between more than one list.

Greg

Oct 27 '05 #9
"Dan Tsafrir" <da***@cs.huji. ac.il> wrote in message
news:43******** ******@cs.huji. ac.il...
TIT wrote:
Dan Tsafrir sade:
is the following code standard? (cleanly compiles under g++-4.0.2):

struct Asc { bool operator()(int a, int b) {return a < b;} };
struct Des { bool operator()(int a, int b) {return b > a;} };
int main() {
int arr[] = {1, 2, 3};
set<int,Asc> asc(arr, arr+3);
set<int,Des>::i terator beg = asc.begin(); //[*]
set<int,Des>::i terator end = asc.end(); //[*]
copy(beg, end, ostream_iterato r<int>(cout, "")); // prints 123
return 0;
}
[...] However, wouldn't it be great had such an iterator assignment been
standard?
I'm suggesting this because it would have allowed iteration over a
collection
of objects sorted according to an arbitrary criterion, without the
overhead of
virtual function calls:

For example, assume you have a collection of N "Job" objects sorted
according to various criteria:

set<Job,cmp_run time> s1; // holds N jobs
set<Job,cmp_arr ival> s2; // holds the same N jobs
... // etc.
Would keeping the jobs in one place, and having sets of iterators work for
you application?

typedef vector<Job> Jobs;
Jobs jobs;

typedef set<Jobs::itera tor, cmp_runtime> S1;
typedef set<Jobs::itera tor, cmp_arrival> S2;

Now the iterators stored in S1 and S2 are the same type.

(Of course cmp_runtime and cmp_arrival now take Jobs::iterator types.)
And you want some loop to iterate through the job collection according
to a criterion which is specified by a user parameter 'p'. Wouldn't being
able to do the following be very beneficial in terms of performance:

set<Job>::const _iterator beg, end;
if( p == RUNTIME ) {
beg = s1.begin();
end = s1.end();
}
else if( p == ARRIVAL ) {
beg = s2.begin();
end = s2.end();
}
else if( ... ) {
// ...
} // ...

for(set<Job>::c onst_iterator i=beg; i!=end; ++i) {
// do whatever...
}


How about:

if (p == RUNTIME)
{
for_each(s1.beg in(), /* ... */);
}
else if (/* ... */)
{
for_each(s2.beg in(), /* ... */);
}
// etc.

Ali

Oct 27 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
1332
by: Dave Benjamin | last post by:
Hola, I made a backport of sets.py that will run on Jython 2.1. Here is a diff against the Python 2.3 version of sets.py. The changes were simple, but I may have made a mistake here or there, and since the unit tests depend on generators, it was too much trouble to try to test the module this way. I'd appreciate any help on testing it more thoroughly. So far, everything seems to be working fine. The majority of the changes were due to...
6
2113
by: Dave Benjamin | last post by:
Hey good people, I've been doing a lot of simultaneous Jython and CPython programming lately, and just wanted to say, with no intended ill will toward any of the individuals who have been generous enough to make the two languages possible, that, well, they're kinda different. I guess it was inevitable, but with Jython stuck at Python 2.1, it's not really the same language as CPython is today. You still have to type "from __future__...
4
2508
by: Scott Smedley | last post by:
Hi all, I'm trying to write a special adaptor iterator for my program. I have *almost* succeeded, though it fails under some circumstances. See the for-loop in main(). Any pointers/help would be muchly appreciated. Apologies for the long post - I couldn't find a shorter way to
2
2529
by: James Stroud | last post by:
Hello All, I find myself in this situation from time to time: I want to compare two lists of arbitrary objects and (1) find those unique to the first list, (2) find those unique to the second list, (3) find those that overlap. But here is the catch: comparison is not straight-forward. For example, I will want to compare 2 objects based on a set of common attributes. These two objects need not be members of the same class, etc. A function...
2
2014
by: Lorenzo Castelli | last post by:
This is an old problem of mine. Basically I have an abstract base class which represents a generic iterator over a collection of elements, and various derived classes that implement the traversing on specific data structures. For each iterator I want to be able to specify the four possible const combinations, corresponding to the various void* for pointers, with the possibility to perform conversions from non-const to const just like...
21
5724
by: T.A. | last post by:
I understand why it is not safe to inherit from STL containers, but I have found (in SGI STL documentation) that for example bidirectional_iterator class can be used to create your own iterator classes by inheriting from it, ie. class my_bidirectional_iterator : public bidirectional_iterator<double> { ... };
1
6551
by: JosAH | last post by:
Greetings, Introduction This week I'll write a bit about generics (those funny angular brackets). I need an example and decided to use sets and some of their operations. This weeks' article discusses one set class and two interesting operations on the set: combinations and set partitioning. First a description of the algorithms is given and then we'll have some fun with the generic implementation of them.
5
1580
by: subramanian100in | last post by:
I am copying the following text as it is from Stroustrup's TC++PL 3rd edition, page 450. It says: "Note that a constructor given two arguments of the same type can be a match for more than one constructor. For example, vector<intv(10, 50); // (size, value) or (iterator1, iterator2)?
20
2467
by: Mr.SpOOn | last post by:
Hi, I need a structure to represent a set of integers. I also need to perform on this set some basic set operations, such as adding or removing elements, joining with other sets and checking for the presence of specific elements. I think that using Python sets would be the best choice, but I also need integers to be ordered inside the set and I've just found out that, instead, Python sets are unordered collections.
0
10433
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
10161
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10000
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9035
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7538
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5436
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
4112
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3720
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2919
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.