2017-04-20 106 views
1

我是C++編程的新手,嘗試通過R來試驗Rcpp。 我創建了一個函數來從字符串中產生所有可能的k-mers。它的工作原理以及在它的串行形式:未定義引用自定義函數(C++和RcppParallel)

#include <Rcpp.h> 
#include <string> 
#include <iostream> 
#include <ctime> 
// using namespace Rcpp; 

// [[Rcpp::export]] 
std::vector<std::string> cpp_kmer(std::string s, int k){ 
    std::vector<std::string> kmers; 
    int seq_loop_size = s.length() - k+1; 
    for (int z=0; z < seq_loop_size; z++) { 
    std::string kmer; 
    kmer = s.substr(z, k); 
    kmers.push_back(kmer) ; 
    } 
    return kmers; 
} 

然而,當我嘗試在並行實現使用此功能(使用RcppParallel),用下面的代碼:

#include <Rcpp.h> 
#include <string> 
#include <iostream> 
#include <ctime> 
using namespace Rcpp; 

// [[Rcpp::depends(RcppParallel)]] 
#include <RcppParallel.h> 
using namespace RcppParallel; 

struct p_cpp_kmer : public Worker { 
    // input string 
    std::vector<std::string> seqs; 
    int k; 
    std::vector<std::string> cpp_kmer(std::string s, int k); 
    // destination list 
    List output; 
    std::string 
    sub_s; 
    // initialize with source and destination 
    p_cpp_kmer(std::vector<std::string> seqs, int k, List output) 
    : seqs(seqs), k(k), output(output) {} 

    // calculate k-mers for the range of sequences requested 
    void operator()(std::size_t begin, std::size_t end) { 
    for (std::size_t i = begin; i < end; i++) 
     sub_s = seqs[i]; 
     cpp_kmer(sub_s, k); 
    } 
}; 

// [[Rcpp::export]] 
List par_cpp_kmer(std::vector<std::string> seqs, int k, bool v){ 
    // allocate output list 
    List outpar(num_seqs); 
    int num_seqs = seqs.size(); 
    // p_cpp_kmer functor (pass input and output matrixes) 
    p_cpp_kmer par_kmer(seqs, k, outpar); 
    parallelFor(0, num_seqs, par_kmer); 
    return wrap(outpar); 
} 

std::vector<std::string> cpp_kmer(std::string s, int k){ 
    std::vector<std::string> kmers; 
    int seq_loop_size = s.length() - k+1; 
    for (int z=0; z < seq_loop_size; z++) { 
    std::string kmer; 
    kmer = s.substr(z, k); 
    kmers.push_back(kmer) ; 
    } 
    return kmers; 
} 

它沒有編譯,給出:未定義的引用p_cpp_kmer :: cpp_kmer(std :: string,int)'錯誤。

我知道它與聲明/引用cpp_kmer有關,但我無法找到適當的地方/如何做(因爲我缺乏C++知識)。

非常感謝您提前。

回答

2

會發生什麼事是你p_cpp_kmer結構聲明瞭cpp_kmer方法,但它永遠不會定義。相反,後面定義的是免費功能cpp_kmer

將此方法聲明

std::vector<std::string> cpp_kmer(std::string s, int k); 

你似乎想用它:

void operator()(std::size_t begin, std::size_t end) { 
    for (std::size_t i = begin; i < end; i++) 
    sub_s = seqs[i]; 
    cpp_kmer(sub_s, k); 
} 

但不是您定義的自由功能cpp_kmer這裏:

std::vector<std::string> cpp_kmer(std::string s, int k){ 
    std::vector<std::string> kmers; 
    int seq_loop_size = s.length() - k+1; 
    for (int z=0; z < seq_loop_size; z++) { 
    std::string kmer; 
    kmer = s.substr(z, k); 
    kmers.push_back(kmer) ; 
    } 
    return kmers; 
} 

你既可以刪除結構中的cpp_kmer方法的定義,以便自由函數被使用,或者實際上定義它。

還有其他問題的代碼:

  • 在你operator()你放棄的結果。我猜你的意思是有這個代替output[i] = cpp_kmer(sub_s, k);

  • 即使你做的東西像上面的代碼是不安全的,因爲output[i] = cpp_kmer(sub_s, k);分配[R對象(每個單獨的R串與串矢量),不能在一個單獨的線程發生。

如果您確實想要並行執行此操作,則需要確保不要在工作中分配任何R對象。

此外,當考慮使用C++ 11和底層爲RcppParallel的tbb庫時,編寫並行代碼要容易得多。例如:

#include <Rcpp.h> 
#include <RcppParallel.h> 

using namespace Rcpp; 
using namespace RcppParallel; 

// [[Rcpp::depends(RcppParallel)]] 
// [[Rcpp::plugins(cpp11)]] 

using string_vector = std::vector<std::string> ; 
using list_string_vector = std::vector<string_vector> ; 

// [[Rcpp::export]] 
list_string_vector par_cpp_kmer(string_vector seqs, int k, bool v){ 
    int num_seqs = seqs.size() ; 

    list_string_vector out(num_seqs) ; 

    tbb::parallel_for(0, num_seqs, 1, [&seqs,k,&out](int i){ 
    std::string& s = seqs[i] ; 
    int seq_loop_size = s.length() - k+1; 

    std::vector<std::string> vec(seq_loop_size) ; 
    for (int z=0; z < seq_loop_size; z++) { 
     vec[z] = s.substr(z, k); 
    } 
    out[i] = vec ; 

    }) ; 
    return out ; 
} 

這是假設std::string可以在單獨的線程分配:

> par_cpp_kmer(c("foobar", "blabla"), 3) 
[[1]] 
[1] "foo" "oob" "oba" "bar" 

[[2]] 
[1] "bla" "lab" "abl" "bla" 
+0

非常感謝你,你的解決方案看起來很簡單,但是當試圖編譯它時,我收到以下錯誤:'cpp_kmer_par_SO.cpp:10:7:錯誤:'string_vector'之前預期的嵌套名稱說明符' cpp_kmer_par_SO.cpp :10:7:錯誤:'string_vector'尚未聲明 cpp_kmer_par_SO.cpp:10:21:錯誤:預計';'在'='標記之前 和'list_string_vector'聲明的類似錯誤。 我證實我可以使用[Rcpp gallery](http://gallery.rcpp.org/articles/first-steps-with-C++11/)中的示例編譯其他C++ 11代碼。 – IsoBar

+0

關於使代碼「線程安全」,我試圖使用RVector 方法,如[這裏](https://rcppcore.github.io/RcppParallel/#safe_accessors)所述,但我不能'找到使用這個結構的字符串而不是數字的任何例子。 – IsoBar

+0

您使用'expected nested-name-specifier'指定的錯誤表明您可能沒有完整的C++ 11實現。也許你可以使用一些'typedef'來代替。 –

0

對於不同的結構(或公共名稱空間),您可能有一個針對cpp_kmer的實現,但是您的struct p_cpp_kmer缺少成員函數cpp_kmer的實現。你將需要添加一個實現,如:

std::vector<std::string> p_cpp_kmer::cpp_kmer(std::string s, int k) { 
    // your implementation goes here 
} 
+0

@StephanLechter,你的意思是移動cpp_kmer的p_cpp_kmer結構內部的整個實施? – IsoBar