CGAL::Delaunay_d C++ on UCI Seeds dataset: “EXC_BAD_ACCESS” - c++
For my PhD work, I need to construct the Delaunay triangulation (DT) of a given point set in any (low) dimension. So far, I have been using the C++ CGAL library with data up to 4D without any noticeable problem.
However, as I used the same class CGAL::Delaunay_d as I previously used on an 7D data set (namely UCI repository Seeds data set ), it seems like something is going wrong and I don't know how to trace my problem.
Here is a copy-pastable code to reproduce the execution:
// CGAL includes
#include <CGAL/Cartesian_d.h>
#include <CGAL/Delaunay_d.h>
#include <CGAL/Gmpq.h>
// STANDARD includes
#include <iostream>
#include <string>
#include <map>
// TYPEDEFS
typedef CGAL::Gmpq EXACT_RT;
typedef CGAL::Cartesian_d<EXACT_RT> EXACT_Kernel;
typedef EXACT_Kernel::Point_d EXACT_Point;
typedef EXACT_Kernel::Vector_d EXACT_Vector;
typedef CGAL::Delaunay_d<EXACT_Kernel> EXACT_Delaunay_any_d;
typedef EXACT_Delaunay_any_d::Vertex_handle EXACT_Vertex_handle;
// NAMESPACES
using namespace std;
using namespace CGAL;
// FUNCTIONS
int main(int argc, char *argv[]);
void delaunay_d(EXACT_Delaunay_any_d &DT, const map <unsigned, vector<EXACT_RT> > &data);
map <unsigned, vector<EXACT_RT> > data_parse(const string &data_set);
// DATASET
char seeds_data_char[] = "15,26 14,84 0,871 5,763 3,312 2,221 5,22\n\
14,88 14,57 0,8811 5,554 3,333 1,018 4,956\n\
14,29 14,09 0,905 5,291 3,337 2,699 4,825\n\
13,84 13,94 0,8955 5,324 3,379 2,259 4,805\n\
16,14 14,99 0,9034 5,658 3,562 1,355 5,175\n\
14,38 14,21 0,8951 5,386 3,312 2,462 4,956\n\
14,69 14,49 0,8799 5,563 3,259 3,586 5,219\n\
14,11 14,1 0,8911 5,42 3,302 2,7 5\n\
16,63 15,46 0,8747 6,053 3,465 2,04 5,877\n\
16,44 15,25 0,888 5,884 3,505 1,969 5,533\n\
15,26 14,85 0,8696 5,714 3,242 4,543 5,314\n\
14,03 14,16 0,8796 5,438 3,201 1,717 5,001\n\
13,89 14,02 0,888 5,439 3,199 3,986 4,738\n\
13,78 14,06 0,8759 5,479 3,156 3,136 4,872\n\
13,74 14,05 0,8744 5,482 3,114 2,932 4,825\n\
14,59 14,28 0,8993 5,351 3,333 4,185 4,781\n\
13,99 13,83 0,9183 5,119 3,383 5,234 4,781\n\
15,69 14,75 0,9058 5,527 3,514 1,599 5,046\n\
14,7 14,21 0,9153 5,205 3,466 1,767 4,649\n\
12,72 13,57 0,8686 5,226 3,049 4,102 4,914\n\
14,16 14,4 0,8584 5,658 3,129 3,072 5,176\n\
14,11 14,26 0,8722 5,52 3,168 2,688 5,219\n\
15,88 14,9 0,8988 5,618 3,507 0,7651 5,091\n\
12,08 13,23 0,8664 5,099 2,936 1,415 4,961\n\
15,01 14,76 0,8657 5,789 3,245 1,791 5,001\n\
16,19 15,16 0,8849 5,833 3,421 0,903 5,307\n\
13,02 13,76 0,8641 5,395 3,026 3,373 4,825\n\
12,74 13,67 0,8564 5,395 2,956 2,504 4,869\n\
14,11 14,18 0,882 5,541 3,221 2,754 5,038\n\
13,45 14,02 0,8604 5,516 3,065 3,531 5,097\n\
13,16 13,82 0,8662 5,454 2,975 0,8551 5,056\n\
15,49 14,94 0,8724 5,757 3,371 3,412 5,228\n\
14,09 14,41 0,8529 5,717 3,186 3,92 5,299\n\
13,94 14,17 0,8728 5,585 3,15 2,124 5,012\n\
15,05 14,68 0,8779 5,712 3,328 2,129 5,36\n\
16,12 15 0,9 5,709 3,485 2,27 5,443\n\
16,2 15,27 0,8734 5,826 3,464 2,823 5,527\n\
17,08 15,38 0,9079 5,832 3,683 2,956 5,484\n\
14,8 14,52 0,8823 5,656 3,288 3,112 5,309\n\
14,28 14,17 0,8944 5,397 3,298 6,685 5,001\n\
13,54 13,85 0,8871 5,348 3,156 2,587 5,178\n\
13,5 13,85 0,8852 5,351 3,158 2,249 5,176\n\
13,16 13,55 0,9009 5,138 3,201 2,461 4,783\n\
15,5 14,86 0,882 5,877 3,396 4,711 5,528\n\
15,11 14,54 0,8986 5,579 3,462 3,128 5,18\n\
13,8 14,04 0,8794 5,376 3,155 1,56 4,961\n\
15,36 14,76 0,8861 5,701 3,393 1,367 5,132\n\
14,99 14,56 0,8883 5,57 3,377 2,958 5,175\n\
14,79 14,52 0,8819 5,545 3,291 2,704 5,111\n\
14,86 14,67 0,8676 5,678 3,258 2,129 5,351\n\
14,43 14,4 0,8751 5,585 3,272 3,975 5,144\n\
15,78 14,91 0,8923 5,674 3,434 5,593 5,136\n\
14,49 14,61 0,8538 5,715 3,113 4,116 5,396\n\
14,33 14,28 0,8831 5,504 3,199 3,328 5,224\n\
14,52 14,6 0,8557 5,741 3,113 1,481 5,487\n\
15,03 14,77 0,8658 5,702 3,212 1,933 5,439\n\
14,46 14,35 0,8818 5,388 3,377 2,802 5,044\n\
14,92 14,43 0,9006 5,384 3,412 1,142 5,088\n\
15,38 14,77 0,8857 5,662 3,419 1,999 5,222\n\
12,11 13,47 0,8392 5,159 3,032 1,502 4,519\n\
11,42 12,86 0,8683 5,008 2,85 2,7 4,607\n\
11,23 12,63 0,884 4,902 2,879 2,269 4,703\n\
12,36 13,19 0,8923 5,076 3,042 3,22 4,605\n\
13,22 13,84 0,868 5,395 3,07 4,157 5,088\n\
12,78 13,57 0,8716 5,262 3,026 1,176 4,782\n\
12,88 13,5 0,8879 5,139 3,119 2,352 4,607\n\
14,34 14,37 0,8726 5,63 3,19 1,313 5,15\n\
14,01 14,29 0,8625 5,609 3,158 2,217 5,132\n\
14,37 14,39 0,8726 5,569 3,153 1,464 5,3\n\
12,73 13,75 0,8458 5,412 2,882 3,533 5,067\n\
17,63 15,98 0,8673 6,191 3,561 4,076 6,06\n\
16,84 15,67 0,8623 5,998 3,484 4,675 5,877\n\
17,26 15,73 0,8763 5,978 3,594 4,539 5,791\n\
19,11 16,26 0,9081 6,154 3,93 2,936 6,079\n\
16,82 15,51 0,8786 6,017 3,486 4,004 5,841\n\
16,77 15,62 0,8638 5,927 3,438 4,92 5,795\n\
17,32 15,91 0,8599 6,064 3,403 3,824 5,922\n\
20,71 17,23 0,8763 6,579 3,814 4,451 6,451\n\
18,94 16,49 0,875 6,445 3,639 5,064 6,362\n\
17,12 15,55 0,8892 5,85 3,566 2,858 5,746\n\
16,53 15,34 0,8823 5,875 3,467 5,532 5,88\n\
18,72 16,19 0,8977 6,006 3,857 5,324 5,879\n\
20,2 16,89 0,8894 6,285 3,864 5,173 6,187\n\
19,57 16,74 0,8779 6,384 3,772 1,472 6,273\n\
19,51 16,71 0,878 6,366 3,801 2,962 6,185\n\
18,27 16,09 0,887 6,173 3,651 2,443 6,197\n\
18,88 16,26 0,8969 6,084 3,764 1,649 6,109\n\
18,98 16,66 0,859 6,549 3,67 3,691 6,498\n\
21,18 17,21 0,8989 6,573 4,033 5,78 6,231\n\
20,88 17,05 0,9031 6,45 4,032 5,016 6,321\n\
20,1 16,99 0,8746 6,581 3,785 1,955 6,449\n\
18,76 16,2 0,8984 6,172 3,796 3,12 6,053\n\
18,81 16,29 0,8906 6,272 3,693 3,237 6,053\n\
18,59 16,05 0,9066 6,037 3,86 6,001 5,877\n\
18,36 16,52 0,8452 6,666 3,485 4,933 6,448\n\
16,87 15,65 0,8648 6,139 3,463 3,696 5,967\n\
19,31 16,59 0,8815 6,341 3,81 3,477 6,238\n\
18,98 16,57 0,8687 6,449 3,552 2,144 6,453\n\
18,17 16,26 0,8637 6,271 3,512 2,853 6,273\n\
18,72 16,34 0,881 6,219 3,684 2,188 6,097\n\
16,41 15,25 0,8866 5,718 3,525 4,217 5,618\n\
17,99 15,86 0,8992 5,89 3,694 2,068 5,837\n\
19,46 16,5 0,8985 6,113 3,892 4,308 6,009\n\
19,18 16,63 0,8717 6,369 3,681 3,357 6,229\n\
18,95 16,42 0,8829 6,248 3,755 3,368 6,148\n\
18,83 16,29 0,8917 6,037 3,786 2,553 5,879\n\
18,85 16,17 0,9056 6,152 3,806 2,843 6,2\n\
17,63 15,86 0,88 6,033 3,573 3,747 5,929\n\
19,94 16,92 0,8752 6,675 3,763 3,252 6,55\n\
18,55 16,22 0,8865 6,153 3,674 1,738 5,894\n\
18,45 16,12 0,8921 6,107 3,769 2,235 5,794\n\
19,38 16,72 0,8716 6,303 3,791 3,678 5,965\n\
19,13 16,31 0,9035 6,183 3,902 2,109 5,924\n\
19,14 16,61 0,8722 6,259 3,737 6,682 6,053\n\
20,97 17,25 0,8859 6,563 3,991 4,677 6,316\n\
19,06 16,45 0,8854 6,416 3,719 2,248 6,163\n\
18,96 16,2 0,9077 6,051 3,897 4,334 5,75\n\
19,15 16,45 0,889 6,245 3,815 3,084 6,185\n\
18,89 16,23 0,9008 6,227 3,769 3,639 5,966\n\
20,03 16,9 0,8811 6,493 3,857 3,063 6,32\n\
20,24 16,91 0,8897 6,315 3,962 5,901 6,188\n\
18,14 16,12 0,8772 6,059 3,563 3,619 6,011\n\
16,17 15,38 0,8588 5,762 3,387 4,286 5,703\n\
18,43 15,97 0,9077 5,98 3,771 2,984 5,905\n\
15,99 14,89 0,9064 5,363 3,582 3,336 5,144\n\
18,75 16,18 0,8999 6,111 3,869 4,188 5,992\n\
18,65 16,41 0,8698 6,285 3,594 4,391 6,102\n\
17,98 15,85 0,8993 5,979 3,687 2,257 5,919\n\
20,16 17,03 0,8735 6,513 3,773 1,91 6,185\n\
17,55 15,66 0,8991 5,791 3,69 5,366 5,661\n\
18,3 15,89 0,9108 5,979 3,755 2,837 5,962\n\
18,94 16,32 0,8942 6,144 3,825 2,908 5,949\n\
15,38 14,9 0,8706 5,884 3,268 4,462 5,795\n\
16,16 15,33 0,8644 5,845 3,395 4,266 5,795\n\
15,56 14,89 0,8823 5,776 3,408 4,972 5,847\n\
15,38 14,66 0,899 5,477 3,465 3,6 5,439\n\
17,36 15,76 0,8785 6,145 3,574 3,526 5,971\n\
15,57 15,15 0,8527 5,92 3,231 2,64 5,879\n\
15,6 15,11 0,858 5,832 3,286 2,725 5,752\n\
16,23 15,18 0,885 5,872 3,472 3,769 5,922\n\
13,07 13,92 0,848 5,472 2,994 5,304 5,395\n\
13,32 13,94 0,8613 5,541 3,073 7,035 5,44\n\
13,34 13,95 0,862 5,389 3,074 5,995 5,307\n\
12,22 13,32 0,8652 5,224 2,967 5,469 5,221\n\
11,82 13,4 0,8274 5,314 2,777 4,471 5,178\n\
11,21 13,13 0,8167 5,279 2,687 6,169 5,275\n\
11,43 13,13 0,8335 5,176 2,719 2,221 5,132\n\
12,49 13,46 0,8658 5,267 2,967 4,421 5,002\n\
12,7 13,71 0,8491 5,386 2,911 3,26 5,316\n\
10,79 12,93 0,8107 5,317 2,648 5,462 5,194\n\
11,83 13,23 0,8496 5,263 2,84 5,195 5,307\n\
12,01 13,52 0,8249 5,405 2,776 6,992 5,27\n\
12,26 13,6 0,8333 5,408 2,833 4,756 5,36\n\
11,18 13,04 0,8266 5,22 2,693 3,332 5,001\n\
11,36 13,05 0,8382 5,175 2,755 4,048 5,263\n\
11,19 13,05 0,8253 5,25 2,675 5,813 5,219\n\
11,34 12,87 0,8596 5,053 2,849 3,347 5,003\n\
12,13 13,73 0,8081 5,394 2,745 4,825 5,22\n\
11,75 13,52 0,8082 5,444 2,678 4,378 5,31\n\
11,49 13,22 0,8263 5,304 2,695 5,388 5,31\n\
12,54 13,67 0,8425 5,451 2,879 3,082 5,491\n\
12,02 13,33 0,8503 5,35 2,81 4,271 5,308\n\
12,05 13,41 0,8416 5,267 2,847 4,988 5,046\n\
12,55 13,57 0,8558 5,333 2,968 4,419 5,176\n\
11,14 12,79 0,8558 5,011 2,794 6,388 5,049\n\
12,1 13,15 0,8793 5,105 2,941 2,201 5,056\n\
12,44 13,59 0,8462 5,319 2,897 4,924 5,27\n\
12,15 13,45 0,8443 5,417 2,837 3,638 5,338\n\
11,35 13,12 0,8291 5,176 2,668 4,337 5,132\n\
11,24 13 0,8359 5,09 2,715 3,521 5,088\n\
11,02 13 0,8189 5,325 2,701 6,735 5,163\n\
11,55 13,1 0,8455 5,167 2,845 6,715 4,956\n\
11,27 12,97 0,8419 5,088 2,763 4,309 5\n\
11,4 13,08 0,8375 5,136 2,763 5,588 5,089\n\
10,83 12,96 0,8099 5,278 2,641 5,182 5,185\n\
10,8 12,57 0,859 4,981 2,821 4,773 5,063\n\
11,26 13,01 0,8355 5,186 2,71 5,335 5,092\n\
10,74 12,73 0,8329 5,145 2,642 4,702 4,963\n\
11,48 13,05 0,8473 5,18 2,758 5,876 5,002\n\
12,21 13,47 0,8453 5,357 2,893 1,661 5,178\n\
11,41 12,95 0,856 5,09 2,775 4,957 4,825\n\
12,46 13,41 0,8706 5,236 3,017 4,987 5,147\n\
12,19 13,36 0,8579 5,24 2,909 4,857 5,158\n\
11,65 13,07 0,8575 5,108 2,85 5,209 5,135\n\
12,89 13,77 0,8541 5,495 3,026 6,185 5,316\n\
11,56 13,31 0,8198 5,363 2,683 4,062 5,182\n\
11,81 13,45 0,8198 5,413 2,716 4,898 5,352\n\
10,91 12,8 0,8372 5,088 2,675 4,179 4,956\n\
11,23 12,82 0,8594 5,089 2,821 7,524 4,957\n\
10,59 12,41 0,8648 4,899 2,787 4,975 4,794\n\
10,93 12,8 0,839 5,046 2,717 5,398 5,045\n\
11,27 12,86 0,8563 5,091 2,804 3,985 5,001\n\
11,87 13,02 0,8795 5,132 2,953 3,597 5,132\n\
10,82 12,83 0,8256 5,18 2,63 4,853 5,089\n\
12,11 13,27 0,8639 5,236 2,975 4,132 5,012\n\
12,8 13,47 0,886 5,16 3,126 4,873 4,914\n\
12,79 13,53 0,8786 5,224 3,054 5,483 4,958\n\
13,37 13,78 0,8849 5,32 3,128 4,67 5,091\n\
12,62 13,67 0,8481 5,41 2,911 3,306 5,231\n\
12,76 13,38 0,8964 5,073 3,155 2,828 4,83\n\
12,38 13,44 0,8609 5,219 2,989 5,472 5,045\n\
12,67 13,32 0,8977 4,984 3,135 2,3 4,745\n\
11,18 12,72 0,868 5,009 2,81 4,051 4,828\n\
12,7 13,41 0,8874 5,183 3,091 8,456 5\n\
12,37 13,47 0,8567 5,204 2,96 3,919 5,001\n\
12,19 13,2 0,8783 5,137 2,981 3,631 4,87\n\
11,23 12,88 0,8511 5,14 2,795 4,325 5,003\n\
13,2 13,66 0,8883 5,236 3,232 8,315 5,056\n\
11,84 13,21 0,8521 5,175 2,836 3,598 5,044\n\
12,3 13,34 0,8684 5,243 2,974 5,637 5,063";
//////////
// MAIN //
//////////
int main(int argc, char *argv[]) {
// DATA SET declaration
string seeds_data(seeds_data_char);
map <unsigned, vector<EXACT_RT> > my_DATA = data_parse(seeds_data);
// DT declaration
EXACT_Delaunay_any_d my_DT(7);
// DT construction
delaunay_d(my_DT, my_DATA);
return 0;
}
// DELAUNAY TRIANGULATION function
void delaunay_d(
EXACT_Delaunay_any_d &DT,
const map <unsigned, vector<EXACT_RT> > &data)
{
// Dim size variable
int d = ((data.begin()) ->second).size();
int i = 1;
// Scanning data set -- DT construction
for(map <unsigned, vector<EXACT_RT> >::const_iterator it = data.begin(); it != data.end(); it++, i++){
// Constructing Point objects
EXACT_Point tmp = EXACT_Point(d, (it ->second).begin(), (it ->second).end());
// Inserting point in the triangulation
EXACT_Vertex_handle v_tmp = DT.insert(tmp);
// DEBUG
std::cout << "-- DEBUG POST -- " << i << " -- DT.all_simplices().size() : " << DT.all_simplices().size() << " -- DT.current_dimension() : " << DT.current_dimension() << endl;
}
}
// PARSING DATA function
map <unsigned, vector<EXACT_RT> > data_parse(
const string &data_set)
{
// RETURNED map
map <unsigned, vector<EXACT_RT> > result;
// TMP variables declaration
vector<EXACT_RT> vect;
string tmp_value;
char current_char;
for (unsigned i=0; i<data_set.length(); i++)
{
current_char = data_set[i];
// Testing if read character is tab or space (i.e. end of a number) ...
if( (current_char == '\t') || (current_char == ' ')) {
double curr_num = atof(tmp_value.c_str());
vect.push_back(EXACT_RT(curr_num)); // Storing the double value.
tmp_value.clear(); // Clearing current number
}
// ... end of a line ...
else
if ( (current_char == '\n') || (current_char == '\r') ) {
double curr_num = atof(tmp_value.c_str());
vect.push_back(EXACT_RT(curr_num)); // Storing the double value.
result.insert ( pair <unsigned, vector<EXACT_RT> > (i++, vect) ); // Feeding returned map
tmp_value.clear(); // Clearing current number
vect.clear(); // Clearing the vector containing the converted values
}
// .. storing any other character
else {
// Dealing with decimal character (from ',' to '.')
if(current_char == ',') {
// Storing current character
tmp_value.push_back('.');
}
else
// Storing current character
tmp_value.push_back(current_char);
}
}
return result;
}
As I used exact number type CGAL::Gmpq for the computation of the DT, I suspect an internal bug of CGAL but I can't assert it. My error actually occurs within the call of function EXACT_Delaunay_any_d::insert()and I don't know how to find a way to debug it.
An “EXC_BAD_ACCESS” signal stops my program while trying to insert the 78-th point, after construction of 20926 simplices.
My questions are:
Should I use some other exact number type ?
Is it an internal issue of CGAL function EXACT_Delaunay_any_d::insert() ?
Is it a problem of memory allocation related to my OS (Mac OS X 10.6.8) ?
Thanks in advance if you have any answer / clue for investigation !
Octavio
Interestingly, you just reported a stack overflow on stackoverflow.com.
The function visibility_search in Convex_hull_d.h is recursive (not a terminal recursion) and the depth of the recursion is apparently not bounded. This is a bug. You should be able to get a bit further by increasing the stack size (the procedure is explained in other questions on this site). Let us know how that fares.
You can also try to reduce other stack use. Maybe using mpq_class or CGAL::Quotient<CGAL::MP_Float> instead of CGAL::Gmpq would help, or it might be even worse. You could also recompile the GMP library after replacing 65536 with 1024 in gmp-impl.h.
Related
Initialization of Auxillary Property Maps in Boost Graph Library
Objective Generate a random spanning tree on a randomly generated graph. Why: because I don't know yet if I can directly generate random trees with specific number of nodes or leaves in BGL. My problem I think it boils down to struggling initializing the Auxillary Property Maps to a sensical default. As you will see I've tried a number of combination of solutions, in the current state the code fails to compile with a cannot form a reference to 'void'. What I tried template<class Graph, class Generator> auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) { // create empty graph Graph g; using vertex_t = typename Graph::vertex_descriptor; using edge_t = typename Graph::edge_descriptor; // The key and value types of the map must both be the graph's vertex type. using predecessor_map_t = boost::static_property_map<vertex_t, vertex_t>; predecessor_map_t predecessor_map = boost::make_static_property_map<vertex_t,vertex_t>(vertex_t()); // unweighted version selected by passing an object of type static_property_map<double> as the weight map, so let's go using weight_map_t = boost::static_property_map< double >; // weight_map_t weight_map = boost::make_transform_value_property_map([](edge_t& e) { return 1.0; }, get(boost::edge_bundle, g)); // nope //weight_map_t weight_map = boost::make_static_property_map<double>(1.0); // yes but complicated // weight_map_t weight_map; // nope: why isn't it default constructible? double my_constant_weight = 1.0; weight_map_t weight_map(my_constant_weight); using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type; color_map_t color_map ; // i suspect this is faulty // mutate graph boost::generate_random_graph(g, n_vertices, n_edges, rng); // pick root, I guess we could as well pick 1st vertex auto root = boost::random_vertex(g, rng); boost::random_spanning_tree(g, rng, root, predecessor_map, weight_map, color_map); return g; }
First: Your own suspect using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type; color_map_t color_map; // i suspect this is faulty Yes. PropertyMaps map properties. They are like references. Here, color_map is essentially an unitialized reference. You need something like color_map_t color_map = get(boost::vertex_color, g); This, of course, assumes that a vertex_color_t property map has been associated with the graph by traits. In other words, this assumes that the property is an iternal property of the graph. Internal properties are often used by default. Second: A constant cannot be modified You use a static property map: auto predecessor_map = boost::make_static_property_map<vertex_t, vertex_t>(vertex_t()); That just creates a "virtual" property map (without a backing data structure) that returns the construction parameter on every key. Logically, the return value is constant. However, predecessor map is an output parameter: You will need an LValuePropertyMap there. E.g. std::map<vertex_t, vertex_t> predecessors; auto predecessor_map =boost::make_assoc_property_map(predecessors); Or even auto vindex = get(boost::vertex_index, g); auto predecessors = std::vector<vertex_t>(num_vertices(g)); auto predecessor_map = boost::make_safe_iterator_property_map( predecessors.begin(), predecessors.size(), vindex); Which uses a vertex index to (optionally) translate descriptors into vector indices. Note that the second is fixed-size, so initialize it after creating all vertices. Other Points Of Interest // weight_map_t weight_map; // nope: why isn't it default constructible? What would it do? Surely it won't default to what you think is a good default (1.0). So I'd just write auto weight_map = boost::static_property_map(1.0); Simplified I'd write the entire function as: template <class Graph, class Generator> auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) { using vertex_t = typename Graph::vertex_descriptor; Graph g; generate_random_graph(g, n_vertices, n_edges, rng); std::map<vertex_t, vertex_t> predecessors; random_spanning_tree(g, rng, random_vertex(g, rng), boost::make_assoc_property_map(predecessors), boost::static_property_map(1.0), // unweighted get(boost::vertex_color, g)); return g; } Functional Problems You're asking some good questions yourself. But let me start with some observations. You have Unspecified/Undefined Behaviour because your input graph doesn't conform to the requirements: There must be a path from every non-root vertex of the graph to the root; the algorithm typically enters an infinite loop when given a graph that does not satisfy this property, but may also throw the exception loop_erased_random_walk_stuck if the search reaches a vertex with no outgoing edges Indeed, running your code only completes for a few random seeds, and fails or runs infinitely for others (this is even increasing the chance of satisfying the requirements by using undirectedS): Listing while true; do (set -x; time ./build/sotest& sleep 3; kill %1); done You are creating the random spanning tree only to completely forget about it. Did you intend to return the predecessor map as well (or a derived path representation)? Your own questions: "cannot form a reference to 'void'" Usually indicates an associated property map could not be found (e.g. what happens if you fail to supply the vertex_color interior property. In this case the remedy is simply to use the default color map: random_spanning_tree( g, rng, boost::root_vertex(random_vertex(g, rng)) .predecessor_map(boost::make_assoc_property_map(predecessors)) .weight_map(boost::static_property_map(1.0)) // unweighted ); "I don't know yet if I can directly generate random trees with specific number of nodes or leaves in BGL." You can generate random graphs with specific number of nodes and leaves - as you already demonstrate. trees. You can also find random spanning trees (given a graph satisfying the preconditions). To adhere to the preconditions the simplest way would be to generate undirected graphs, whilst additionally making sure that the result is connected. A simple, possible inefficient(?) way to ensure it would be to explicitly connect components: if (int n = boost::connected_components(ug, cmap); n > 1) { std::cout << "Connecting " << n << " components:\n"; for (int c = 1; c < n; ++c) std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n"; } It might be more effective to write your own generating algorithm. BONUS EXAMPLE Showing the use of connected_components to make sure the graph is fully connected, and even building a directed tree from undirected source graph. Also writing graphviz representations of the "raw" (undirected) source and "tree" (directed spanning tree), it seems to work pretty well. Live On Coliru #include <boost/graph/adjacency_list.hpp> #include <boost/graph/connected_components.hpp> #include <boost/graph/graph_utility.hpp> #include <boost/graph/graphviz.hpp> #include <boost/graph/random.hpp> #include <boost/graph/random_spanning_tree.hpp> #include <boost/property_map/function_property_map.hpp> #include <iomanip> #include <random> namespace detail { template <typename T> struct make_undirected { using type = void; }; template <typename A, typename B, typename C, typename D, typename E, typename F> struct make_undirected<boost::adjacency_list<A, B, C, D, E, F>> { using type = boost::adjacency_list<A, B, boost::undirectedS, D, E, F>; }; } // namespace detail template <typename T> using Undirect = typename detail::make_undirected<T>::type; template <class Graph, class Generator> auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) { using UG = Undirect<Graph>; using vertex_t = typename UG::vertex_descriptor; // assuming integral vertex index for simplicity static_assert(std::is_same_v<vertex_t, size_t>); static_assert(std::is_same_v<typename UG::vertex_descriptor, typename Graph::vertex_descriptor>); UG ug; generate_random_graph(ug, n_vertices, n_edges, rng); vertex_t const root = random_vertex(ug, rng); print_graph(ug, std::cout << "Raw root: " << root << ", graph:\n"); { // make connected std::map<vertex_t, int> components; auto from = [&](int component) { // just picking the first... for (auto& [v, c] : components) if (c == component) return v; throw std::range_error("component"); }; auto cmap = boost::make_assoc_property_map(components); if (int n = connected_components(ug, cmap); n > 1) { std::cout << "Connecting " << n << " components:\n"; for (int c = 1; c < n; ++c) std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n"; } } std::map<vertex_t, vertex_t> predecessors; random_spanning_tree( ug, rng, boost::root_vertex(root) // .predecessor_map(boost::make_assoc_property_map(predecessors))); Graph tree(num_vertices(ug)); // build a tree copy for (auto v : boost::make_iterator_range(vertices(ug))) if (predecessors.contains(v)) if (auto pred = predecessors.at(v); ug.null_vertex() != pred) add_edge(predecessors.at(v), v, tree); auto save = [&predecessors](auto& g, auto name) { using edge_t = typename std::decay_t<decltype(g)>::edge_descriptor; auto tree_edge = [&predecessors](auto s, auto t) { auto it = predecessors.find(s); return it != end(predecessors) && it->second == t; }; boost::dynamic_properties dp; dp.property("node_id", get(boost::vertex_index, g)); dp.property("color", boost::make_function_property_map<edge_t>([tree_edge, &g](edge_t e) { auto s = source(e, g), t = target(e, g); return tree_edge(s, t) || tree_edge(t, s) ? "red" : "gray"; })); std::ofstream os(name); write_graphviz_dp(os, g, dp); }; save(ug, "raw.dot"); save(tree, "tree.dot"); return std::pair(std::move(tree), root); } int main(int argc, char** argv) { using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS>; auto const seed = argc > 1 ? std::stoull(argv[1]) : std::random_device{}(); std::cout << "seed: " << seed << std::endl; std::mt19937 prng(seed); auto [tree, root] = generate_random_spanning_tree<G>(10, 20, prng); print_graph(tree, std::cout << "From root: " << root << ", tree:\n"); } Prints the sample seed: 1577455792 Raw root: 7, graph: 0 <--> 7 2 3 7 5 2 8 1 <--> 2 <--> 8 0 4 0 4 9 3 <--> 9 5 0 8 4 <--> 7 7 2 2 5 5 <--> 8 3 0 4 6 <--> 7 <--> 4 4 0 8 0 8 <--> 2 5 7 9 0 3 9 <--> 3 8 2 Connecting 3 components: Added (0,1) Added (1,6) From root: 7, tree: 0 --> 1 3 1 --> 6 2 --> 9 3 --> 5 4 --> 2 5 --> 4 8 6 --> 7 --> 0 8 --> 9 --> Running locally with: watch './build/sotest; for a in raw tree; do (set -x ; dot -Tpng -o $a.png $a.dot); done' Shows random solutions like:
C++, efficient way to call many possible functions from user
I'm relatively new to c++, mostly worked with python. I have a scenario where a user(me) uses a GUI to send commands to a microcontroller via serial, and then the microcontroller processes them. Right now i have 10 commands, but as the project develops (some form of modular robot) I can envision having 50-100 possible commands. Is there a better way for my c++ handleCommands function to select which one of the possible 100 functions to run without doing massive case switches or if else statements? Extract of the code: char cmd = 1; // example place holder int value = 10; //example place holder switch (cmd){ case '1': toggleBlink(value); break; case '2': getID(value); // in this case value gets ignored by the function as its not required break; This works fine for 3-4 functions but doesn't seem to me like the best way to do it for more functions. I've heard of lookup tables but as each function is different and may require arguments or not I'm consumed on how to implement them. Some background on the set-up: The commands are mainly diagnostic ,< ID > ect and a couple of functional ones that require parameters like, <blink,10> <runto,90> <set-mode,locked> The validation is done in python against a csv file and the actual serial message sent to the microcontroller is sent as <(index of comand in csvfile),parameter> with < > and , being delimiters. So the user would type blink,10 and the python app will send <1,10> over serial as blink is found at index 1 of the csv file. The microcontroller reads these in and i am left over with 2 char arrays, the command array containing a number, and the value array containing the value sent.(also a number) As I'm running this on a microcontroller i don't really want to have to store a long file of possible commands in flash, hence the validation done on the python gui side. Note that in the case of a possible multi argument function, say <move,90,30> i.e move 90 degrees in 30 seconds eat, the actual function would only receive one argument "30,90" and then split that up as needed.
If you have the commands comming over the serial line in the format <command-mapped-to-a-number,...comma-separated-parameters...> we can simulate that like so: #include <iostream> #include <sstream> // needed for simple parsing #include <string> #include <unordered_map> // needed for mapping of commands to functors int main() { std::cout << std::boolalpha; // example commands lines read from serial: for (auto& cmdline : {"<1,10>", "<2,10,90>", "<3,locked>", "<4>"}) { std::cout << exec(cmdline) << '\n'; } } exec above is the interpreter that will return true if the command line was parsed and executed ok. In the examples above, command 1 takes one parameter, 2 takes two, 3 takes one (string) and 4 doesn't have a parameter. The mapping from command-mapped-to-a-number could be an enum: // uint8_t has room for 256 commands, make it uint16_t to get room for 65536 commands enum class command_t : uint8_t { blink = 1, take_two = 2, set_mode = 3, no_param = 4, }; and exec would make the most basic validation of the command line (checking < and >) and put it in a std::istringstream for easy extraction of the information on this command line: bool exec(const std::string& cmdline) { if(cmdline.size() < 2 || cmdline.front() != '<' || cmdline.back() != '>' ) return false; // put all but `<` and `>` in an istringstream: std::istringstream is(cmdline.substr(1,cmdline.size()-2)); // extract the command number if (int cmd; is >> cmd) { // look-up the command number in an `unordered_map` that is mapped to a functor // that takes a reference to an `istringstream` as an argument: if (auto cit = commands.find(command_t(cmd)); cit != commands.end()) { // call the correct functor with the rest of the command line // so that it can extract, validate and use the arguments: return cit->second(is); } return false; // command look-up failed } return false; // command number extraction failed } The only tricky part left is the unordered_map of commands and functors. Here's a start: // a helper to eat commas from the command line struct comma_eater {} comma; std::istream& operator>>(std::istream& is, const comma_eater&) { // next character must be a comma or else the istream's failbit is set if(is.peek() == ',') is.ignore(); else is.setstate(std::ios::failbit); return is; } std::unordered_map<command_t, bool (*)(std::istringstream&)> commands{ {command_t::blink, [](std::istringstream& is) { if (int i; is >> comma >> i && is.eof()) { std::cout << "<blink," << i << "> "; return true; } return false; }}, {command_t::take_two, [](std::istringstream& is) { if (int a, b; is >> comma >> a >> comma >> b && is.eof()) { std::cout << "<take-two," << a << ',' << b << "> "; return true; } return false; }}, {command_t::set_mode, [](std::istringstream& is) { if (std::string mode; is >> comma && std::getline(is, mode,',') && is.eof()) { std::cout << "<set-mode," << mode << "> "; return true; } return false; }}, {command_t::no_param, [](std::istringstream& is) { if (is.eof()) { std::cout << "<no-param> "; return true; } return false; }}, }; If you put that together you'll get the below output from the successful parsing (and execution) of all command lines received: <blink,10> true <take-two,10,90> true <set-mode,locked> true <no-param> true Here's a live demo.
Given an integer index for each "command" a simple function pointer look-up table can be used. For example: #include <cstdio> namespace { // Command functions (dummy examples) int examleCmdFunctionNoArgs() ; int examleCmdFunction1Arg( int arg1 ) ; int examleCmdFunction2Args( int arg1, int arg2 ) ; int examleCmdFunction3Args( int arg1, int arg2, arg3 ) ; int examleCmdFunction4Args( int arg1, int arg2, int arg3, int arg4 ) ; const int MAX_ARGS = 4 ; const int MAX_CMD_LEN = 32 ; typedef int (*tCmdFn)( int, int, int, int ) ; // Symbol table #define CMD( f ) reinterpret_cast<tCmdFn>(f) static const tCmdFn cmd_lookup[] = { 0, // Invalid command CMD( examleCmdFunctionNoArgs ), CMD( examleCmdFunction1Arg ), CMD( examleCmdFunction2Args ), CMD( examleCmdFunction3Args ), CMD( examleCmdFunction4Args ) } ; } namespace cmd { // For commands of the form: "<cmd_index[,arg1[,arg2[,arg3[,arg4]]]]>" // i.e an angle bracketed comma-delimited sequence commprising a command // index followed by zero or morearguments. // e.g.: "<1,123,456,0>" int execute( const char* command ) { int ret = 0 ; int argv[MAX_ARGS] = {0} ; int cmd_index = 0 ; int tokens = std::sscanf( "<%d,%d,%d,%d,%d>", command, &cmd_index, &argv[0], &argv[1], &argv[2], &argv[3] ) ; if( tokens > 0 && cmd_index < sizeof(cmd_lookup) / sizeof(*cmd_lookup) ) { if( cmd_index > 0 ) { ret = cmd_lookup[cmd_index]( argv[0], argv[1], argv[2], argv[3] ) ; } } return ret ; } } The command execution passes four arguments (you can expand that as necessary) but for command functions taking fewer arguments they will simply be "dummy" arguments that will be ignored. Your proposed translation to an index is somewhat error prone and maintenance heavy since it requires you to maintain both the PC application symbol table and the embedded look up table in sync. It may not be prohibitive to have the symbol table on the embedded target; for example: #include <cstdio> #include <cstring> namespace { // Command functions (dummy examples) int examleCmdFunctionNoArgs() ; int examleCmdFunction1Arg( int arg1 ) ; int examleCmdFunction2Args( int arg1, int arg2 ) ; int examleCmdFunction3Args( int arg1, int arg2, arg3 ) ; int examleCmdFunction4Args( int arg1, int arg2, int arg3, int arg4 ) ; const int MAX_ARGS = 4 ; const int MAX_CMD_LEN = 32 ; typedef int (*tCmdFn)( int, int, int, int ) ; // Symbol table #define SYM( c, f ) {#c, reinterpret_cast<tCmdFn>(f)} static const struct { const char* symbol ; const tCmdFn command ; } symbol_table[] = { SYM( cmd0, examleCmdFunctionNoArgs ), SYM( cmd1, examleCmdFunction1Arg ), SYM( cmd2, examleCmdFunction2Args ), SYM( cmd3, examleCmdFunction3Args ), SYM( cmd4, examleCmdFunction4Args ) } ; } namespace cmd { // For commands of the form: "cmd[ arg1[, arg2[, arg3[, arg4]]]]" // i.e a command string followed by zero or more comma-delimited arguments // e.g.: "cmd3 123, 456, 0" int execute( const char* command_line ) { int ret = 0 ; int argv[MAX_ARGS] = {0} ; char cmd[MAX_CMD_LEN + 1] ; int tokens = std::sscanf( "%s %d,%d,%d,%d", command_line, cmd, &argv[0], &argv[1], &argv[2], &argv[3] ) ; if( tokens > 0 ) { bool cmd_found = false ; for( int i = 0; !cmd_found && i < sizeof(symbol_table) / sizeof(*symbol_table); i++ ) { cmd_found = std::strcmp( cmd, symbol_table[i].symbol ) == 0 ; if( cmd_found ) { ret = symbol_table[i].command( argv[0], argv[1], argv[2], argv[3] ) ; } } } return ret ; } } For very large symbol tables you might want a more sophisticated look-up, but depending on the required performance and determinism, the simple exhaustive search will be sufficient - far faster than the time taken to send the serial data. Whilst the resource requirement for the symbol table is somewhat higher that the indexed look-up, it is nonetheless ROM-able and will can be be located in Flash memory which on most MCUs is a less scarce resource than SRAM. Being static const the linker/compiler will most likely place the tables in ROM without any specific directive - though you should check the link map or the toolchain documentation n this. In both cases I have defined the command functions and executer as returning an int. That is optional of course, but you might use that for returning responses to the PC issuing the serial command.
What you are talking about are remote procedure calls. So you need to have some mechanism to serialize and un-serialize the calls. As mentioned in the comments you can make a map from cmd to the function implementing the command. Or simply an array. But the problem remains that different functions will want different arguments. So my suggestion would be to add a wrapper function using vardiac templates. Prefix every command with the length of data for the command so the receiver can read a block of data for the command and knows when to dispatch it to a function. The wrapper then takes the block of data, splits it into the right size for each argument and converts it and then calls the read function. Now you can make a map or array of those wrapper function, each one bound to one command and the compiler will generate the un-serialize code for you from the types. (You still have to do it once for each type, the compiler only combines those for the full function call).
std::map pass by reference Pointer to Object
I'm coding a plugin for XPLANE10 which gets a MSG from ROS. My IDE is QTcreator 4.1.0 based QT 5.7.0 for Ubuntu 64 Bit. I would like to use C++11 Standards My code explained The main initializes ROS and creates a map -> container. ROS spins in a loop till my GUI sends a MSG where my AirPlane should fly. The MSG contains 3 floats(phi, theta, psi) where "phi" is the AirPlane ID, theta contains the ID for my ETA(Estimated Time of Arrival) and psi contains the ID for my pose All of the IDs are saved in the ParameterServer(lookuptable). So at the beginning i look up the activeAirplanes which returns a vector . I would like to store them in a map where the key is the AirCraft ID and the second param is an instance of the Object. So i have initialized the for example(looked in container while debugging): [0] first = 1 // Airplane ID1 [0] second = new CObject(freq) [1] first = 2 // Airplane ID2 [1] second = new CObject(freq) If i get a MSG from GUI phi = 1 theta=2 psi=3 , ROS will callback MSG(....std::map<i32, CObject> &container) // if phi is 1 so use the mapkey 1 and trigger the method do_stuff from CObject do_stuff(phi, theta, psi,freq) I would like to call the in a function from main int getPlanes(std::map<i32,CObject>& container) { ... getActiveAirplanesFromServer(activePlanes); } First Question: How do i pass the container to my callback? Second Question: How do i parallelize do_stuff() so my callback will return to main and i'm able to command more aircrafts while the others are calculated? Third Question: How would be the correct syntax for getPlanes to pass the container by reference so getPlanes() can edit it? Fourth Question: Is there a difference between std::map<i32,CObject*> map std::map<i32,CObject>* map and std::map<i32,CObject*>::iterator it=container->begin(); std::map<i32,CObject*>::iterator* it=container->begin(); If yes, what do i want ? #4Solved // I have to edit stuff 'cause of some restrictions in my company. #include "Header.h" int main() { f64 freq = 10; std::map<i32, CObject>* container; std::map<i32,CObject>::iterator* it=container->begin(); // ROS if(!ros::isInitialized()) { int rosargc = 0; char** rosargv = NULL; ros::init(rosargc, rosargv, "MainNode");//), ros::init_options::AnonymousName); } else { printf("Ros has already been initialized.....\n"); } ros::NodeHandle* mainNodeHandle=new ros::NodeHandle; ros::AsyncSpinner spinner(2); ParameterServer * ptrParam= new ParameterServer(mainNodeHandle); ros::Subscriber airSub=mainNodeHandle->subscribe<own_msgs::ownStruct>("/MSG", 1000, boost::bind(MSG, _1, freq, container)); std::vector<i32> activePlanes; i32 retVal=0; retVal += ptrParam-> ParameterServer::getActiveAirplanesFromServer(activePlanes); if (retVal == 0 && activePlanes.size()>0) { for (u32 j =0; j <activePlanes.size(); j++) { container->insert (std::pair<i32,CObject> (activePlanes[j] , new CObject(freq))); } } while (ros::ok()) { spinner.start(); //spinnt sehr viel :-) ros::waitForShutdown (); } std::cout<<"ENDE"<<std::endl; int retval = 1; return retval; } void MSG(const own_msgs::ownStruct<std::allocator<void> >::ConstPtr &guiMSG, f64 freq, std::map<i32, CObject> &container) { if ((guiMSG->phi != 0) && (guiMSG->theta != 0) && (guiMSG->psi != 0)) { std::string alpha = std::to_string(guiMSG->phi)+std::to_string(guiMSG->theta)+to_string(guiMSG->psi); container.at(guiMSG->phi) -> do_stuff(guiMSG->phi,guiMSG->theta,guiMSG->psi, freq); } else { std::cout<<" Did not receive anything\n"<<endl; } } void do_stuff(...) { //copy the IDs to private Member of this single Object //setROS() for this single Object //callback the current AC pose via ID from XPLANE //callback the wished AC pose via ID from ParamServer // do some calculations for optimum flight path // publish the Route to XPlane } EDIT:: Problem is i get it to compile now and if debug it and set a breakpoint at : void MSG(const own_msgs::ownStruct<std::allocator<void> >::ConstPtr &guiMSG,f64 freq,std::map<i32, CObject*> &container) { .. /*->*/ container.at(guiMSG->)... } The Container remains empty.
So i read some stuff about pointers and i saw my errors.. I confused * and & if i want to pass the adress of a variable i have to write like int main() { int a = 0; AddTwo(&a) cout<<a<<endl; // Output: 2 } void AddTwo(int* a) { a+=2; }
How to pass array of integers from Tcl to C++? [closed]
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers. Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance. Closed 9 years ago. Improve this question How do I send Tcl array to C++? I have written the following code: Tcl: set ns [new Simulator] set n [$ns node] $n set X_ 100 $n set Y_ 30 $n set Z_ 0 set x [$n set X_] set y [$n set Y_] set z [$n set Z_] #after 2000 set b {12 2 3 4 5} set aa [new "Application/Trust/ITLeach"] $aa set bufer_ 1 $aa set allnode_ $n $aa set X_ $x $aa set Y_ $y $aa set Z_ $z $aa set ClausterHeadID_ [array get b] **#send array to c++** $ns at 0.0 "$aa start" puts $b $ns run ITLEACH.h: #ifndef ns_ITLeach_h #define ns_ITLeach_h #include "app.h" #include "node.h" #include "tcl.h" #include "mobilenode.h" #include <iostream> #include <fstream> class ITLeach; #define TCL_OK 0 class ITLeach : public Application { public: ITLeach(); virtual int command(int argc, const char*const* argv); protected: // need to define recv and timeout void start(); int Buffer; MobileNode * node ; ofstream nodeSetting; double XPos ; double YPos ; double ZPos ; int ClausterHeadID [] ; //int array that passed from tcl file int ClausterID [] ; int id_node; }; #endif ITLEACH.cc: /* * ITLeach.cc * * Created on: Oct 29, 2013 * Author: root */ #include "ITLeach.h" static class ITLeachClass : public TclClass { public: ITLeachClass() : TclClass("Application/Trust/ITLeach") {} TclObject* create(int, const char*const*) { return (new ITLeach()); } } class_app_ITLeach; ITLeach::ITLeach() : Application() { Tcl_Obj *baObj = Tcl_NewObj(); bind("bufer_",&Buffer); bind("allnode_",&node); bind("X_",&XPos); bind("Y_",&YPos); bind("Z_",&ZPos); bind("ClausterHeadID_",(int *) &ClausterHeadID); // call array from tcl bind("ClausterID_",ClausterID); bind("id_",&id_node); } int ITLeach::command(int argc, const char*const* argv) { if (strcmp(argv[1], "start") == 0) { ITLeach::start(); return(TCL_OK); } return(ITLeach::command(argc, argv)); } void ITLeach::start() { //double x=0, y =0 , z =0; nodeSetting.open("./leachnode.txt",fstream::app); //node = (MobileNode*)Node::get_node_by_address(i); //node->location()->getLocation(x,y,z); //node->getLoc(&x,&y,&z); nodeSetting << "id " << id_node << " x "<< XPos << " y " << YPos << " z " << ZPos <<"\n"; nodeSetting.close(); printf(" claster head number : %d \n" ,ClausterHeadID[1]); printf("node number is : %d \n",Buffer); } I send array from Tcl with this code: $aa set ClausterHeadID_ [array get b] **#send array to c++** and receive array from C++ with this code: bind("ClausterHeadID_",(int *) &ClausterHeadID); // call array from tcl But it doesn't work, please help me.
If you've got that command bound to the string interface (i.e., the arguments arrive via int argc, char **argv) then you use Tcl_SplitList() to take apart the relevant argument (which might be argv[argc-1], i.e., the last argument) and then Tcl_GetInt() to retrieve an integer from each of those values. Those integers are the members of that Tcl list. int listc; char **listv; if (Tcl_SplitList(interp, argv[argc-1], &listc, &listv) != TCL_OK) { // wasn't a valid list! return TCL_ERROR; } std::vector<int> theArray(listc, 0); for (int i=0 ; i<listc ; i++) { if (Tcl_GetInt(interp, listv[i], &theArray[i]) != TCL_OK) { // wasn't an int in the list! return TCL_ERROR; } } This isn't very fast! For a faster way, you need to use the Tcl_Obj-based API (the Tcl_Obj is the fundamental Tcl first-class value type), starting with registering your implementation function correctly. After that, it's fairly easy to convert the above code: int listc; Tcl_Obj **listv; if (Tcl_ListObjGetElements(interp, argv[argc-1], &listc, &listv) != TCL_OK) { // wasn't a valid list! return TCL_ERROR; } std::vector<int> theArray(listc, 0); for (int i=0 ; i<listc ; i++) { if (Tcl_GetIntFromObj(interp, listv[i], &theArray[i]) != TCL_OK) { // wasn't an int in the list! return TCL_ERROR; } } The big difference? A Tcl_Obj knows whether it is holding a string or an integer (or a float or any number of other things) and so the Tcl runtime doesn't normally need to reparse or type-convert values, whereas if everything is a string, you do a lot of conversions. (It's common to say “Everything is a string” in Tcl, but that's inaccurate; the correct version is “Everything has a perfect string serialization, or is a named entity” but that's rather more verbose.)
C++ STL Set: Cannot find() last element inserted
I am in the process of writing an application in which I use the Set class in the C++ STL. I've discovered that the call to set->find() always seems to fail when I query for the last element I inserted. However, if I iterate over the set, I am able to see the element I was originally querying for. To try to get a grasp on what is going wrong, I've created a sample application that exhibits the same behavior that I am seeing. My test code is posted below. For the actual application itself, I need to store pointers to objects in the set. Is this what is causing the weird behavior. Or is there an operator I need to overload in the class I am storing the pointer of? Any help would be appreciated. #include <stdio.h> #include <set> using namespace std; #define MySet set<FileInfo *,bool(*)(const FileInfo *, const FileInfo*)> class FileInfo { public: FileInfo() { m_fileName = 0; } FileInfo( const FileInfo & file ) { setFile( file.getFile() ); } ~FileInfo() { if( m_fileName ) { delete m_fileName; m_fileName = 0; } } void setFile( const char * file ) { if( m_fileName ) { delete m_fileName; } m_fileName = new char[ strlen( file ) + 1 ]; strcpy( m_fileName, file ); } const char * getFile() const { return m_fileName; } private: char * m_fileName; }; bool fileinfo_comparator( const FileInfo * f1, const FileInfo* f2 ) { if( f1 && ! f2 ) return -1; if( !f1 && f2 ) return 1; if( !f1 && !f2 ) return 0; return strcmp( f1->getFile(), f2->getFile() ); } void find( MySet *s, FileInfo * value ) { MySet::iterator iter = s->find( value ); if( iter != s->end() ) { printf( "Found File[%s] at Item[%p]\n", (*iter)->getFile(), *iter ); } else { printf( "No Item found for File[%s]\n", value->getFile() ); } } int main() { MySet *theSet = new MySet(fileinfo_comparator); FileInfo * profile = new FileInfo(); FileInfo * shell = new FileInfo(); FileInfo * mail = new FileInfo(); profile->setFile( "/export/home/lm/profile" ); shell->setFile( "/export/home/lm/shell" ); mail->setFile( "/export/home/lm/mail" ); theSet->insert( profile ); theSet->insert( shell ); theSet->insert( mail ); find( theSet, profile ); FileInfo * newProfile = new FileInfo( *profile ); find( theSet, newProfile ); FileInfo * newMail = new FileInfo( *mail ); find( theSet, newMail ); printf( "\nDisplaying Contents of Set:\n" ); for( MySet::iterator iter = theSet->begin(); iter != theSet->end(); ++iter ) { printf( "Item [%p] - File [%s]\n", *iter, (*iter)->getFile() ); } } The Output I get from this is: Found File[/export/home/lm/profile] at Item[2d458] Found File[/export/home/lm/profile] at Item[2d458] No Item found for File[/export/home/lm/mail] Displaying Contents of Set: Item [2d478] - File [/export/home/lm/mail] Item [2d468] - File [/export/home/lm/shell] Item [2d458] - File [/export/home/lm/profile] **Edit It's kind of sad that I have to add this. But as I mentioned before, this is a sample application that was pulled from different parts of a larger application to exhibit the failure I was receiving. It is meant as a unit test for calling set::find on a set populated with heap allocated pointers. If you have a problem with all the new()s, I'm open to suggestions on how to magically populate a set with heap allocated pointers without using them. Otherwise commenting on "too many new() calls" will just make you look silly. Please focus on the actual problem that was occurring (which is now solved). Thanks. ***Edit Perhaps I should have put these in my original question. But I was hoping there would be more focus on the problem with the find() (or as it turns out fileinfo_comparator function that acts more like strcmp than less), then a code review of a copy-paste PoC unit test. Here are some points about the code in the full application itself. FileInfo holds a lot of data along with the filename. It holds SHA1 sums, file size, mod time, system state at last edit, among other things. I have cut out must of it's code for this post. It violates the Rule of 3 in this form (Thanks #Martin York. See comments for wiki link). The use of char* over std::string was originally chosen because of the use of 3rd_party APIs that accept char*. The app has since evolved from then. Changing this is not an option. The data inside FileInfo is polled from a named pipe on the system and is stored in a Singleton for access across many threads. (I would have scope issues if I didn't allocate on heap) I chose to store pointers in the Set because the FileInfo objects are large and constantly being added/removed from the Set. I decided pointers would be better than always copying large structures into the Set. The if statement in my destructor is needless and a left over artifact from debugging of an issue I was tracking down. It should be pulled out because it is unneeded.
Your comparison function is wrong - it returns bool, not integer as strcmp(3). The return statement should be something like: return strcmp( f1->getFile(), f2->getFile() ) < 0; Take a look here. Also, out of curiosity, why not just use std::set<std::string> instead? STL actually has decent defaults and frees you from a lot of manual memory management.
It looks to me like your FileInfo doesn't work correctly (at least for use in a std::set). To be stored in a std::set, the comparison function should return a bool indicating that the two parameters are in order (true) or out of order (false). Given what your FileInfo does (badly designed imitation of std::string), you'd probably be better off without it completely. As far as I can see, you can use std::string in its place without any loss of functionality. You're also using a lot of dynamic allocation for no good reason (and leaking a lot of what you allocate). #include <set> #include <iostream> #include <iterator> #include <string> int main() { char *inputs[] = { "/export/home/lm/profile", "/export/home/lm/shell", "/export/home/lm/mail" }; char *outputs[] = {"Found: ", "Could **not** find: "}; std::set<std::string> MySet(inputs, inputs+3); for (int i=0; i<3; i++) std::cout << outputs[MySet.find(inputs[i]) == MySet.end()] << inputs[i] << "\n"; std::copy(MySet.begin(), MySet.end(), std::ostream_iterator<std::string>(std::cout, "\n")); return 0; } Edit: even when (or really, especially when) FileInfo is more complex, it shouldn't attempt to re-implement string functionality on its own. It should still use an std::string for the file name, and implement an operator< that works with that: class FileInfo { std::string filename; public: // ... bool operator<(FileInfo const &other) const { return filename < other.filename; } FileInfo(char const *name) : filename(name) {} }; std::ostream &operator(std::ostream &os, FileInfo const &fi) { return os << fi.filename; } int main() { // std::set<std::string> MySet(inputs, inputs+3); std:set<FileInfo> MySet(inputs, inputs+3); // ... std::copy(MySet.begin(), MySet.end(), std::ostream_iterator<FileInfo>(std::cout, "\n")); }
In your constructor: FileInfo( const FileInfo & file ) { setFile( file.getFile() ); } m_fileName seems to be not initialized.