2013-02-12 2 views
8

먼저 마이크로 컨트롤러 용 소프트웨어를 만들고 있다고 가정 해 봅시다. RAM 사용에 문제가 없으며 큰 블록의 const 데이터를 비 휘발성 (플래시) 메모리에 저장하는 것이 좋습니다.C++에서 "split object"를 만드는 좋은 방법이 있습니까?

내가 얻으려고하는 것은 C++에서 "분할 된"개체를 만드는 좋은 방법을 찾는 것입니다. 예를 들어, 1 바이트의 데이터 (읽기/쓰기)와이를 액세스하기위한 멀티 바이트 "영수증"이 있다고 가정 해 보겠습니다. "영수증"이 파일 이름 인 긴 문자열이고 그것이 가리키는 미디어가 느린 경우 실제로 각 요청에서 읽는 대신 메모리에있는 단일 바이트를 버퍼링하는 것이 좋습니다.

class Data 
{ 
    uint8_t byte; 
    bool valid; 
    const char filename[128]; 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
} 

이 방법의 명백한 문제는 전체 130 바이트가 RAM으로 끝나고 두 개만 변경해야한다는 것입니다.

class DataNonConst 
{ 
    uint8_t byte; 
    bool valid; 
} 

class DataConst 
{ 
    const char filename[128]; 
    DataNonConst * const data; 
} 

static DataNonConst storage; 
const DataConst holder("some_long_name_here", &storage); 

지금 유일한 문제는 (그래서 두를 만드는 나는 그런 몇 백 싶은 경우를 만드는 과정을 분할 - 객체 즉 : 그래서 분할 객체의 아이디어 왔어요 개체와 두 번째 첫 번째 링크) 꽤 지루하고 문제가 생깁니다 ...

그래서 질문은 - 영리한 C++ 트릭이나 어쩌면 일부 템플릿 마법을 사용하기 쉽게 만드는 좋은 방법이 있습니까? 즉, 하나의 문장으로 두 개의 객체를 만들고 서로 연결하는 방법입니다. 하나의 객체는 숨겨져있는 것이 좋습니다. 저장 프로 시저의 이름을 쉽게 생성 할 수있는 자동화 된 방법이 없기 때문에 여기서는 매크로 솔루션이 가능하지 않다고 생각합니다. 개체는 다른 개체에 포인터를 포함해야하므로 동일한 유형이어야합니다. (하나의 함수는 이들을 작성하는 데 중점을 둡니다. 다른 하나는 읽기에만 신경을 씁니다) ... 내가 생각한 모든 솔루션은 템플릿에 가상 인터페이스를 사용해야합니다 (따라서 vtable 포인터로 객체를 더 크게 만들고 보너스 템플릿을 얻습니다.) 길가에 "바인딩"익명의 변수가 -

실제로 전체 문제의 일부는 더 간단 질문에 줄일 수 있습니다 -bloat) 또는 편집

... 거대한 템플릿 팽창을 초래할 C++의 멤버 필드? 같은 STH : 위의 "희망 사항"홀더에서
const ConstData holder("...", NonConstData()); // this object HAS TO be ROMable 

이 ROM에서 const를 객체이며 익명 객체 NonConstData에 RAM에 "어딘가"만든 어떤 포인터/참조 /가

. 또는 : 내가 수동으로 두 객체를 생성하고 다른 하나를 바인딩하지 수있는 것

std:pair<const ConstData &, NonConstData &> holder(ConstData(), NonConstData()); 

아무것도.

당신은 정수로 포인터를 교체하고이 같은 모든 DataNonConst 객체를 단일 정적 배열, 만들 수 있습니다
+0

필요 무엇? – Ation

+1

ROM이 가능하여 RAM이 아닌 비 휘발성 메모리에 저장됩니다. 이것은 주제에 대해 매우 중요합니다 ... –

+1

매크로 솔루션에 관해서 - 당신은 매크로에 대한 인수로서 스토리지 객체의 이름을 전달할 수 없었습니까? –

답변

2

:이 다른 위해 사용되는 분명히 있지만,

class DataNonConst { 
    uint8_t byte; 
    bool valid; 
}; 

static DataNonConst storages[MAX_DATA]; 

class DataConst { 
    const char filename[128]; 
    const int dataIndex; 
    DataNonConst *data() { 
     return &storages[dataIndex]; 
    } 
}; 

const DataConst holderOne("first_long_name_here", 0); 
const DataConst holderTwo("second_long_name_here", 1); 
const DataConst holderThree("third_long_name_here", 2); 

이러한 접근 방식은 Flyweight Pattern에서 영감을 여기에 목적이있다.

명백한 단점은 중복을 피하기 위해 수동으로 입력 항목을 계산해야한다는 것입니다. 그러나 storages 개체는 하나이므로 개체를 만들 수있는 방법은 없습니다.

+0

DataConst 객체의 DataNonConst에 대한 포인터를 갖는 것보다 더 좋은 방법은 무엇입니까? – dspeyer

+0

@dspeyer - 플래시에 저장된 const 홀더 객체를 통해 기본 저장소에 액세스해야합니다. –

0

const 구성원을 static const으로 간단하게 선언하십시오.

static 구성원은 어떤 경우에도 객체 인스턴스 데이터와 별도로 저장됩니다. 컴파일러는 자동으로 static const 같은 멤버를 ROM에 배치 할 수 있지만 그렇지 않은 경우 강제로 컴파일러 지시문이있을 수 있습니다.

+1

각 분할 객체에 대한 const 요소가 달라야 정적 const가 될 수 없습니다. –

+0

@FreddieChopin : 사실 정적이 아닌 const 멤버는 C 런타임시 const 멤버가 런타임으로 초기화되는'const'의 의미로 인해 ROM에 위치 할 수 없다는 것입니다. 정적 const 데이터 멤버 만 포함하고 클래스를 파생시킬 수 있습니다 필요에 따라 하위 클래스의 non-const 데이터로 변환하십시오. – Clifford

2

지금까지 본 유일한 문제는 "핸들"개체에 char[] 회원이 있다는 것입니다. 어쨌든 문자열 리터럴이 비 휘발성 메모리에있을 것이므로 const char*으로 바꾸면 문제가 해결됩니다.

class Data 
{ 
    const char* filename; 
    uint8_t byte; 
    bool valid; 
    Data(const char* filename_) : filename(filename_), valid(false) {} 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
}; 

Data holder("some_long_name_here"); //name is in the non-volatile memory anyway 

char buffer[128] = "\var\bin\omg"; 
Data holder2(buffer); //runtime names work fine, but have to manage the storage yourself. 

또는, 당신은 교활 수 있도록하려면,이 서식하고 약간 더 혼란 인의 희생에도 메모리의 양을 (저장할 수 있습니다.이는 할 수있는 추가 혜택이있다 static 구성원을 비 휘발성 메모리에 쉽게 추가 할 수 있지만 파일 이름별로 공유하지는 않습니다.이 파일은 일 수 있습니다.은 런타임에 이름이 알려져 있지 않은 파일과 함께 사용할 수 있지만 그렇게하지 않는 것이 좋습니다. .

template<const char* filename> 
class Data 
{ 
    uint8_t byte; 
    bool valid; 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
}; 
Data<"some_long_name_here"> holder; 

extern char buffer[128] = "\var\bin\omg"; 
A<buffer> holder2; //this always refers to the filename currently in buffer 

그러나 정말 분할 된 개체를 원한다면 분할 제안은 내가 생각할 수있는 최상의 대답입니다. 이것은 당신이 오버 헤드를 필요로하지 않는 것을 의미

class Data; 
Data *dataArray; 
char **filenameArray; 

class Data { 
    uint8_t byte; 
    bool valid; 
    char*& filename() { 
    int index = this - dataArray; 
    return filenameArray[index]; 
    } 
public: 
    byte read() { 
    if (!valid) 
     performReallySlowRead(filename(), &byte); 
    valid = true; 
    return byte; 
    } 
    init(char* fn) { 
    filename() = allocateInFlash(strlen(fn)); 
    strdup(filename(), fn); 
    valid = FALSE; 
    } 
}; 

// Somewhere before things get run 
dataArray = allocateInRam(sizeof(Data)*nDatas); 
filenameArray = allocateInFlash(sizeof(char*)*nDatas); 

을하고 터치 할 필요가 없습니다 : 당신이 개체의 최대 수를 기꺼이 경우

+0

unfortunatelly "filename"은 예제 일뿐입니다 (; filename에 대한 포인터는 RAM에 저장되지만,이 토론을 위해 메모리의 각 바이트가 계산된다는 것을 가정 해 봅시다. 왜 분할되어야하는지, 그리고 const가 비 const 부분을 가리키는 이유입니다. –

+0

@FreddieChopin : 예, 첫 번째 샘플에서 _pointer_ 파일 이름은 RAM에 있습니다. 모든 바이트 수가 중요하면 템플릿 예제를 사용하십시오. RAM에 필요한 비트 만 유지합니다. –

+0

당신의 대답은 C++ 11의 시맨틱 및 레퍼런스 참조 이동에 대해 생각하게 만들었습니다. 그걸 사용할 수 있을까요? 예를 들어 DataConst의 생성자는 DataNonConst에 대한 rvalue 참조를 취합니다. DataNonConst holder (DataConst()); 그렇게 할 수 있을까요? –

0

암시 적 포인터로 인덱스를 사용할 수 있습니다 램에서 읽을 플래시. 더 이상 표준 메모리 관리를 사용하지 않으며, 더하기 또는 빼기 일 수 있습니다.

class Data; 
std::map<Data*,string, less<Data*>, FlashAllocator<pair<Data*,string>> fns; 
class Data { 
    uint8_t byte; 
    bool valid; 
public: 
    byte read(){ 
    if (!valid) 
     performReallySlowRead(fns[this],&byte); 
    valid = true; 
    return byte; 
    } 
    Data(string fn) { 
    fns[this] = fn; 
    } 
    ~Data() { 
    fns.erase(this); 
    } 
}; 

지도 사소 오버 헤드를 가지고 있지만, 그것은 순식간에 모든 : 당신이 어떤 순수 플래시 오버 헤드가 기꺼이 경우

0

, 당신은 할 수 있습니다.

+0

플래시가 런타임에 쓰기 가능해야하는지 제대로 확인합니까? –

+0

@FreddieChopin 예. 플래시는 일반적으로 맞습니까? 그렇지 않다면, 파일 이름은 어디서 오는가? – dspeyer

+1

SD 카드로 "플래시"라고 생각한다면 쓰기가 가능하지만 여기서 "플래시"== "코드 읽기 전용 메모리"입니다. 또한 쓰기가 가능하지만 그렇게 쉬운 일은 아닙니다. 아이디어는 컴파일 타임에 객체를 만드는 것입니다. –

1

일부 제한 사항을 사용하면 템플릿을 활용하여 각 ROMable DataConst 개체에서 참조하는 DataNonConst의 정적 인스턴스를 만들 수 있습니다.

제한 :

  • 코드 라인 당 __LINE__ 매크로 때문에에만 존재해야 하나 DataConst 선언을 사용합니다. 또는 __COUNT__ 매크로를 사용할 수있는 경우 __LINE__ 대신이 매크로를 사용하여 행당 여러 개의 선언을 허용 할 수 있습니다.

  • DataNonConst 개체는 항상 0으로 초기화됩니다.

코드 : 홀더의 불변성에 대한

struct DataNonConst 
{ 
    uint8_t byte; 
    bool valid; 
}; 

struct DataConst 
{ 
    const char filename[128]; 
    DataNonConst * const data; 
}; 

namespace { 

template <long n> 
struct DataNonConstHolder 
{ 
    static DataNonConst data; 
}; 

template <long n> DataNonConst DataNonConstHolder<n>::data; 

} 

// If __COUNT__ macro is supported, use it instead of __LINE__ 
#define Initializer(s) { s, &DataNonConstHolder<__LINE__>::data } 

const DataConst dc1 = Initializer("filename1"); 
const DataConst dc2 = Initializer("filename2"); 
관련 문제